<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>walterlv</title>
    <description>吕毅，.NET/UWP/WPF/Roslyn/Windows 开发者，微软最有价值专家，Microsoft MVP</description>
    <link>https://blog.walterlv.com/</link>
    <atom:link href="https://blog.walterlv.com/sitemap.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 28 Aug 2021 09:16:45 +0000</pubDate>
    <lastBuildDate>Sat, 28 Aug 2021 09:16:45 +0000</lastBuildDate>
    <generator>Jekyll v4.0.0</generator>
    
      <item>
        <title>将美化进行到底，把 PowerShell 做成 oh-my-zsh 的样子</title>
        <description>&lt;p&gt;不知你有没有看过 Linux 上 oh-my-zsh 的样子？看过之后你一定会惊叹，原来命令行还能这么玩！然而 Windows 下能这么玩吗？答案是可行的，接下来就来看看怎么玩。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-04-12.png&quot; alt=&quot;借用了下 oh-my-zsh 的官网图片&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Windows 下我们用 oh-my-posh 在 PowerShell 中实现这样的效果。分以下三步走：&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装-oh-my-posh&quot;&gt;安装 oh-my-posh&lt;/h2&gt;

&lt;p&gt;我们需要先以管理员权限启动 PowerShell，以便执行安装操作。（具体是在开始按钮上点击右键，选择“Windows PowerShell (管理员)”。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-09-02.png&quot; alt=&quot;以管理员权限启动 PowerShell&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，运行命令以安装 posh-git，这是 oh-my-posh 的依赖。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Install-Module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;posh-git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果此前没有安装 NuGet 提供程序，则此时会提示安装 NuGet；如果此前没有开启执行任意脚本，此处也会提示执行脚本。&lt;em&gt;如果没有权限执行脚本，可能需要先执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Set-ExecutionPolicy Bypass&lt;/code&gt;。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-18-55.png&quot; alt=&quot;Install-Module posh-git -Scope CurrentUser&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-14-23.png&quot; alt=&quot;安装 NuGet 提供程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-17-03.png&quot; alt=&quot;安装 posh-git&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来，运行命令以安装 oh-my-posh 本身。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Install-Module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oh-my-posh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-18-16.png&quot; alt=&quot;Install-Module oh-my-posh -Scope CurrentUser&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-17-16.png&quot; alt=&quot;安装 oh-my-posh&quot; /&gt;&lt;/p&gt;

&lt;p&gt;自此，oh-my-posh 安装完毕。&lt;/p&gt;

&lt;h2 id=&quot;启用模组并设置主题&quot;&gt;启用模组并设置主题&lt;/h2&gt;

&lt;p&gt;接下来，我们需要启用安装的模组。启用模组的命令是：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Import-Module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oh-my-posh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其实写本文主要就是想体验 zsh 的操作，并看看 git 文件夹的视觉效果。现在我们就试试，输入：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Set-PoshPrompt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;（注意，oh-my-posh 更新后，命令从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Set-Theme&lt;/code&gt; 变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Set-PoshPrompt&lt;/code&gt;。）&lt;/p&gt;

&lt;p&gt;然后按一下空格，按一下 Tab。会发现这时已经可以用方向键来选择参数了！原生 PowerShell 可没有这个功能啊！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-27-38.png&quot; alt=&quot;选择主题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Agnoster&lt;/code&gt; 主题。（这些主题都是 oh-my-posh 带给我们的。）&lt;/p&gt;

&lt;p&gt;接下来我们看看 git 文件夹下的显示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-30-05.png&quot; alt=&quot;git 文件夹的显示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;并没有 zsh 那样的效果。——因为我们缺少专用的字体！&lt;/p&gt;

&lt;p&gt;下一节我们会讲如何安装专为 zsh 效果设计的字体。&lt;/p&gt;

&lt;p&gt;现在，我们需要让 PowerShell 每次启动的时候都能够加载这个模组，所以我们需要设置 profile 文件让它自动启用。&lt;/p&gt;

&lt;p&gt;敲 &lt;code class=&quot;highlighter-rouge&quot;&gt;$profile&lt;/code&gt; 可以让 PowerShell 告诉我们这个文件的路径是什么。当然下图是我的路径，读者的默认在文档路径里的 PowerShell 文件夹下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-21-46.png&quot; alt=&quot;profile 文件路径&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们需要编辑这个文件（如果没有，手动创建一个），然后在里面写下那一句话：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Import-Module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oh-my-posh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set-PoshPrompt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Paradox&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-18-13-36-33.png&quot; alt=&quot;在个人配置文件中的内容&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来，新打开 PowerShell（不需要管理员权限）时就会提示加载了这个文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-24-35.png&quot; alt=&quot;加载个人及系统配置文件&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装字体安装第三方-powershell&quot;&gt;安装字体/安装第三方 PowerShell&lt;/h2&gt;

&lt;p&gt;！！！&lt;strong&gt;重要说明：给 PowerShell 定制字体是一件非常困难的事情，非常困难！！！&lt;/strong&gt; &lt;em&gt;可参见 &lt;a href=&quot;/post/customize-fonts-of-command-window&quot;&gt;自定义 Windows PowerShell 和 cmd 的字体&lt;/a&gt; 感受一下。&lt;/em&gt; &lt;strong&gt;所以，这里更倾向于在安装了字体的情况下使用第三方 PowerShell。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;比如下图是我用 vscode 中带的 PowerShell 的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-15-01-52.png&quot; alt=&quot;PowerShell in vscode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;推荐的其他 PowerShell：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/store/productId/9N0DX20HK701&quot;&gt;Windows Terminal&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PowerShell/PowerShell&quot;&gt;PowerShell Core&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.fosshub.com/ConEmu.html&quot;&gt;ConEmu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://cmder.net/&quot;&gt;cmder - Console Emulator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，如果你执意要跑原生 PowerShell，那也不是没有办法，你可以使用 PowerLine 专为 PowerShell 和 zsh 设计的字体，它们的字体是可以完美跑到 PowerShell 和 PowerShell Core 中的。&lt;/p&gt;

&lt;p&gt;下图是我在 PowerShell Core 中的运行效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-18-13-47-07.png&quot; alt=&quot;PowerLine 字体效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;请在这里下载 PowerLine 字体：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/powerline/fonts&quot;&gt;powerline/fonts: Patched fonts for Powerline users.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;方法是克隆这个仓库，然后在克隆出来的文件夹中找到 Install.ps1 文件，执行它，会发现它会自动为我们安装所有的 PowerLine 字体。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-18-13-39-31.png&quot; alt=&quot;Install.ps1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-18-13-41-05.png&quot; alt=&quot;安装所有的字体&quot; /&gt;&lt;/p&gt;

&lt;p&gt;安装完成之后，在 PowerShell 或者 PowerShell Core 的标题栏上右击选择“属性”，然后选择你想要设置的字体就可以立刻看到效果了。注意，PowerLine 字体都是带有 for PowerLine 后缀的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-18-13-45-44.png&quot; alt=&quot;设置字体&quot; /&gt;&lt;/p&gt;

&lt;p&gt;PowerLine 字体官方文档在这里：&lt;a href=&quot;https://powerline.readthedocs.io/en/master/overview.html&quot;&gt;Overview — Powerline beta documentation&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-26-13-38-19.png&quot; alt=&quot;官方文档中的 PowerLine 字体截图&quot; /&gt;&lt;br /&gt;
▲ 官方文档中的 PowerLine 字体截图&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;官方字体的下载链接：&lt;a href=&quot;https://github.com/powerline/fonts&quot;&gt;powerline/fonts: Patched fonts for Powerline users.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;官方字体的看图预览：&lt;a href=&quot;https://github.com/powerline/fonts/blob/master/samples/All.md&quot;&gt;fonts/All.md at master · powerline/fonts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/powerline/fonts&quot;&gt;powerline/fonts: Patched fonts for Powerline users.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://powerline.readthedocs.io/en/master/overview.html&quot;&gt;Overview — Powerline beta documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chaishiwei.com/blog/1621.html&quot;&gt;告别 Windows 终端的难看难用，从改造 PowerShell 的外观开始 – 老柴的宅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 28 Aug 2021 08:56:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/beautify-powershell-like-zsh.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/beautify-powershell-like-zsh.html</guid>
        
        
        <category>windows</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>使用 WPF 做一个可以逼真地照亮你桌面的高性能阳光</title>
        <description>&lt;p&gt;本文想要做的，可不是随便弄一点阳光的半透明形状，然后简单地放到桌面上，&lt;strong&gt;而是真真正正地要照亮桌面上的窗口元素&lt;/strong&gt;！并且，全程使用 GPU 加速，而且代码超简单。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;效果预览&quot;&gt;效果预览&lt;/h2&gt;

&lt;p&gt;先放上两张动图看看效果，GIF 比较大，如果博客里看不到可以点击下面的小标题下载下来看。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/posts/2021-08-05-sunshine-over-cloud.gif&quot;&gt;阳光扫过云层&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-sunshine-over-cloud.gif&quot; alt=&quot;阳光扫过云层&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/posts/2021-08-05-sunshine-over-visual-studio.gif&quot;&gt;阳光扫过 Visual Studio&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-sunshine-over-visual-studio.gif&quot; alt=&quot;阳光扫过 Visual Studio&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，阳光经过云层时，强烈的光芒与云层的光光部分叠加起来了，让人感觉云层的照亮部分十分刺眼。阳光经过 Visual Studio 的界面时，纯色部分可以看出阳光的外形，高饱和度部分在阳光的照耀下显得格外亮眼。&lt;/p&gt;

&lt;h2 id=&quot;代码实现&quot;&gt;代码实现&lt;/h2&gt;

&lt;p&gt;实现本文效果的代码其实很少，只有以下几步：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;制作一个全透明窗口&lt;/li&gt;
  &lt;li&gt;编写一个像素着色器&lt;/li&gt;
  &lt;li&gt;画一个简单的阳光形状&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过在开始之前，我们先创建一个空白的 WPF 项目吧：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-16-51-03.png&quot; alt=&quot;创建一个空白的 WPF 窗口程序&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第一步制作一个全透明窗口&quot;&gt;第一步：制作一个全透明窗口&lt;/h3&gt;

&lt;p&gt;网上广为流传的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt; 的方式就可以，不过我个人不喜欢，因为性能不好。我更推荐大家使用我另一篇博客里推荐的高性能透明窗口的实现方案：&lt;a href=&quot;/post/wpf-transparent-window-without-allows-transparency&quot;&gt;WPF 制作高性能的透明背景异形窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果现在不想看的，我可以直接把 MainWindow.xaml.cs 的代码贴出来（放心，其他地方不需要写代码）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.DesktopSunshine.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.DesktopSunshine&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.DesktopSunshine&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;WindowStyle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;None&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ResizeMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NoResize&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ResizeBorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CornerRadius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CaptionHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最重要的，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Background=&quot;Transparent&quot;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle=&quot;None&quot;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeMode=&quot;NoResize&quot;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness=&quot;-1&quot;&lt;/code&gt; 这四个属性。&lt;/p&gt;

&lt;p&gt;其他的代码，我只是在做一个普通的窗口而已。大小 240 是为了容纳一个太阳的大小。&lt;/p&gt;

&lt;h3 id=&quot;第二步编写一个像素着色器&quot;&gt;第二步：编写一个像素着色器&lt;/h3&gt;

&lt;p&gt;想了解怎么写像素着色器的，可以阅读我的另一篇博客：&lt;a href=&quot;/post/create-wpf-pixel-shader-effects-using-shazzam-shader-editor&quot;&gt;WPF 像素着色器入门：使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;需要在像素着色器里编写此代码（不想学像素着色器的可以忽略此代码直接往后看）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sampler2D&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;inputSampler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Threshold&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TEXCOORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COLOR&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tex2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputSampler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   	    &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果不想自己编写并编译像素着色器，可以直接下载我已经编译好的 .ps 文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/static/attachments/BinaryAlphaEffect.ps&quot;&gt;BinaryAlphaEffect.ps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载下来的文件（或者你自己编译出来的文件）放到解决方案中的任意位置（本示例中放到了 Assets 文件夹中）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-17-00-45.png&quot; alt=&quot;在解决方案中的位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;重要：&lt;/strong&gt;接着，将 .ps 文件加入到编译。请双击项目（Walterlv.DesktopSunshine）以编辑其项目文件（.csproj）。需要增加的行我已经在下面高亮出来了。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;WinExe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net6.0-windows&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
        &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

++    &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Resource Include=&quot;Assets\**\*.ps&quot; /&amp;gt;
++    &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，编写一个使用此 .ps 文件的 C# 类型。我取名为 BinaryAlphaEffect.cs。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media.Effects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.DesktopSunshine&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BinaryAlphaEffect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ShaderEffect&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterPixelShaderSamplerProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Input&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BinaryAlphaEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThresholdProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BinaryAlphaEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UIPropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PixelShaderConstantCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BinaryAlphaEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PixelShader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PixelShader&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;UriSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/Assets/BinaryAlphaEffect.ps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;UpdateShaderValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;UpdateShaderValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThresholdProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Threshold&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThresholdProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThresholdProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Threshold&lt;/code&gt; 是个二值化的阈值，可用来调阳光效果。我把默认值设为了 1，这样阳光效果最强烈。（实际上其他值的效果惨不忍睹。因为这段代码我本来编写的目的不是在做阳光，只是中间不小心做出了阳光效果，觉得很有意思就教大家一下。）&lt;/p&gt;

&lt;h2 id=&quot;第三步画一个简单的阳光形状&quot;&gt;第三步：画一个简单的阳光形状&lt;/h2&gt;

&lt;p&gt;我只画一个圆来表示阳光的形状（想用其他形状的，自己发挥创意）。于是在 MainWindow.xaml 里添加一点点代码：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &amp;lt;/WindowChrome.WindowChrome&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Border&amp;gt;
++          &amp;lt;Ellipse Fill=&quot;White&quot; Width=&quot;160&quot; Height=&quot;160&quot;&amp;gt;
++              &amp;lt;UIElement.Effect&amp;gt;
++                  &amp;lt;BlurEffect Radius=&quot;40&quot; /&amp;gt;
++              &amp;lt;/UIElement.Effect&amp;gt;
++          &amp;lt;/Ellipse&amp;gt;
++      &amp;lt;/Border&amp;gt;
&lt;/span&gt;    &amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，给圆加了一些模糊效果，这是必要的。&lt;/p&gt;

&lt;p&gt;然后，把我们在第二步里写的像素着色器用上：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Border&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;UIElement.Effect&amp;gt;
++          &amp;lt;local:BinaryAlphaEffect /&amp;gt;
++      &amp;lt;/UIElement.Effect&amp;gt;
&lt;/span&gt;        &amp;lt;Ellipse Fill=&quot;White&quot; Width=&quot;160&quot; Height=&quot;160&quot;&amp;gt;
            &amp;lt;UIElement.Effect&amp;gt;
                &amp;lt;BlurEffect Radius=&quot;40&quot; /&amp;gt;
            &amp;lt;/UIElement.Effect&amp;gt;
        &amp;lt;/Ellipse&amp;gt;
    &amp;lt;/Border&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么至此，所有代码已经完成。&lt;/p&gt;

&lt;p&gt;总结一下，我们写了这些代码：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一个新创建的 WPF 项目模板（包含模板自带的 App.xaml App.xaml.cs MainWindow.xaml MainWindow.xaml.cs AssemblyInfo.cs）&lt;/li&gt;
  &lt;li&gt;下载或编译的 BinaryAlphaEffect.ps 像素着色器文件，和用来使用它的 BinaryAlphaEffect.cs 文件&lt;/li&gt;
  &lt;li&gt;使用 BinaryAlphaEffect 类的 MainWindow.xaml 中的几个 Border 和 Ellipse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;是不是代码量非常少？接下来，就是见证奇迹的时刻。&lt;/p&gt;

&lt;h2 id=&quot;效果与性能&quot;&gt;效果与性能&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/static/posts/2021-08-05-sunshine-over-forest.gif&quot;&gt;阳光扫过 Windows 11 自带壁纸&lt;/a&gt;。在太阳附近，与太阳融为一体；在森林中，阳光被树叶遮挡；在水面，阳光跟随着波光闪耀；在岩石上，阳光把岩石照得通亮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-sunshine-over-forest.gif&quot; alt=&quot;阳光扫过云层&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可以把这个阳光放到任何地方，就算是正在播放的视频前面也依然在每帧中都有实时效果。&lt;/p&gt;

&lt;p&gt;最重要的是——它几乎不消耗性能！因为它在图形渲染管线的像素着色器部分运行，其所有代码都在 GPU 中并行执行，且每次执行仅需不到 10 条指令。你可以看到任务管理器中，它的 CPU 和 GPU 消耗都是 0。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-08-05-17-19-52.png&quot; alt=&quot;性能占用非常低&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Aug 2021 09:20:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-a-realistic-sunshine-on-your-desktop-using-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-a-realistic-sunshine-on-your-desktop-using-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>路径标记语法（Path Markup Syntax）完全教程</title>
        <description>&lt;p&gt;无论是 WPF、UWP 还是 Xamarin、MAUI、WinUI，都有可以绘制任意形状的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt; 类型，它支持一种路径标记语法，可以拟合各种形状。同时，SVG 格式使用的也是完全相同的路径语法，你用文本编辑器打开一个 SVG 格式时也会看到这样的字符串。&lt;/p&gt;

&lt;p&gt;你只需要阅读本文，即可从零开始了解并最终学会路径标记语法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;示例&quot;&gt;示例&lt;/h2&gt;

&lt;p&gt;一开始，我们用一张 SVG 图来看看一个典型的路径字符串是什么样子的：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/posts/2021-07-29-svg-image.svg&quot;&gt;&lt;img src=&quot;/static/posts/2021-07-29-svg-image.svg&quot; alt=&quot;SVG 图&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;你可以点击上面这张图以单独打开它，然后查看网页源代码来观察它的字符串内容。我这里也贴一份：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&amp;gt;&amp;lt;!DOCTYPE svg PUBLIC &quot;-//W3C//DTD SVG 1.1//EN&quot; &quot;http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;svg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100%&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100%&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;viewBox=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 0 20 20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:xlink=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/1999/xlink&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xml:space=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;preserve&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:serif=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.serif.com/&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;g&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;控件&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;g&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ic-备课端-主窗口-工具栏-文字&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;serif:id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ic/备课端/主窗口/工具栏/文字&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;g&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;rect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bounds&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;y=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill:#f00;fill-opacity:0;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;形状结合&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M17,11c-1.342,-0 -3.872,1.385 -4.872,2.385c0.958,1.151 1.381,2.186 1.872,3.615l3,-0l0,-6Zm-5,6c-1.568,-3.869 -4.674,-6 -9,-6l0,6l9,-0Zm-9,-8c4,0 6,1 8,3c2,-2 4,-3 6,-3l0,-5l0.206,0c0.439,0 0.794,0.366 0.794,0.818l-0,12.364c-0.003,0.45 -0.356,0.814 -0.794,0.818l-14.412,0c-0.439,-0 -0.794,-0.366 -0.794,-0.818l-0,-12.364c0.003,-0.45 0.356,-0.814 0.794,-0.818l14.206,-0l0,1l-14,-0l0,4Zm10,-1c-0.663,-0 -1,-0.318 -1,-1c0,-0.682 0.337,-1 1,-1c0.663,-0 1,0.318 1,1c0,0.682 -0.337,1 -1,1Z&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill:#666;fill-opacity:0.8;fill-rule:nonzero;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;path/@d&lt;/code&gt; 里的，就是我们即将学习的路径标记字符串。&lt;/p&gt;

&lt;p&gt;XAML 系的路径标记语法与之只有一点点不同。&lt;/p&gt;

&lt;h2 id=&quot;名称&quot;&gt;名称&lt;/h2&gt;

&lt;p&gt;在 SVG 的&lt;a href=&quot;https://www.w3.org/TR/SVG/paths.html&quot;&gt;解释文档&lt;/a&gt;中，对此语法的称呼为“SVG Path Syntax”（SVG 路径语法）。在 XAML 系语言中，称其为“Path Markup Syntax”（路径标记语法），官方也称其为“Mini-Language”。&lt;/p&gt;

&lt;p&gt;由于 SVG 和 XAML 的路径语法几乎一样，所以学会本文可以直接学会两者的语法。&lt;/p&gt;

&lt;h2 id=&quot;语法syntax&quot;&gt;语法（Syntax）&lt;/h2&gt;

&lt;p&gt;路径标记语法从前往后写下来，遵循“命令-参数-命令-参数-命令-参数-……”这样的要求。让我们再来一个更简单的例子：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;M 10,100 C 10,300 300,-200 300,100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;把解释放进这个字符串的话，是这样：&lt;code class=&quot;highlighter-rouge&quot;&gt;M（命令）10,100（点坐标） C（命令）10,300 300,-200 300,100（三个点坐标）&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在 SVG 路径语法中，一共有如下命令可以使用：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;C&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Z&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;z&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;额外的，XAML 系的路径标记语法还有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;看起来很多，但实际上我们可以做一个分类，这样理解起来会更容易一些：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;起点
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;直线
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;贝塞尔曲线
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;椭圆弧
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;封闭
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Z&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;z&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;先来说说一些共性的知识：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一个路径可以由多段组成，用 &lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; 来指定一个新段的开始&lt;/li&gt;
  &lt;li&gt;大写字母后面跟的参数中，点坐标是绝对坐标；小写字母后面跟的参数中，点坐标是相对坐标&lt;/li&gt;
  &lt;li&gt;如果连续几段都是相同的命令，那么后续可以只写参数而省略命令&lt;/li&gt;
  &lt;li&gt;字符串中间的空格 ` ` 和逗号 &lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt; 是用来分隔参数和点的 X、Y 坐标的，可以混用也可以多写&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面，我们一个一个说：&lt;/p&gt;

&lt;h3 id=&quot;f&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;相比于 SVG 来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 是 XAML 系路径标记语法唯一一个特有的语法。&lt;/p&gt;

&lt;p&gt;带上参数一起，&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 只有三种写法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;省略不写&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F0&lt;/code&gt; 表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;EvenOdd&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F1&lt;/code&gt; 表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nonzero&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;省略不写和 &lt;code class=&quot;highlighter-rouge&quot;&gt;F0&lt;/code&gt; 是相同的含义，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;EvenOdd&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;SVG 中如果要实现相同的效果，需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;path/@style&lt;/code&gt; 属性，即&lt;code class=&quot;highlighter-rouge&quot;&gt;style=&quot;fill-rule:nonzero;&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;起点和终点&quot;&gt;起点和终点&lt;/h3&gt;

&lt;h4 id=&quot;m-m-移动命令&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; 移动命令&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt;（Move，移动）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：开始一段新的路径，然后将起点移到 &lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; 后面的参数中&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;startPoint&lt;/code&gt;（起点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;M10,100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; 后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;startPoint&lt;/code&gt; 参数是绝对点坐标，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; 后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;startPoint&lt;/code&gt; 参数是相对上一个命令中端点坐标的相对点坐标。&lt;/p&gt;

&lt;h4 id=&quot;l-l-h-h-v-v-直线命令&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt; 直线命令&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt;（Line，直线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条直线到此命令的端点&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;L100,200&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt;（Horizontal Line，水平线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条水平直线到此命令的横坐标&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;（横坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;H100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt;（Vertical Line，垂直线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条垂直直线到此命令的纵坐标&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt;（纵坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;V200&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;与前面一样，大写字符后面的坐标和数值是绝对坐标，小写字符后面的坐标和数值是相对坐标。&lt;/p&gt;

&lt;h4 id=&quot;c-cq-qs-st-t-贝塞尔曲线命令&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt; 贝塞尔曲线命令&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt;（Cubic Bezier Curve，三次贝塞尔曲线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条&lt;strong&gt;三次贝塞尔曲线&lt;/strong&gt;到此命令的端点&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint1&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint2&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（控制点坐标1 控制点坐标2 端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;C10,300 300,-200 300,100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt;（Quadratic Bezier Curve，二次贝塞尔曲线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条&lt;strong&gt;二次贝塞尔曲线&lt;/strong&gt;到此命令的端点&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（控制点坐标 端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;Q300,-200 300,100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt;（Smooth Cubic Bezier Curve，平滑三次贝塞尔曲线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条平滑的&lt;strong&gt;三次贝塞尔曲线&lt;/strong&gt;到此命令的端点，确保在上一个点的曲线是连续的&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint2&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（控制点坐标2 端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;S300,-200 300,100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所谓“平滑”，即保证曲线在上一个端点处的的曲线连续而没有突变（一次可导）。而平滑的方法，便是将上一个命令在端点处的贝塞尔控制点相对上一个点进行一次镜像。&lt;/p&gt;

&lt;p&gt;下面这张图可以说明是如何做到平滑的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-29-17-19-10.png&quot; alt=&quot;将控制点镜像&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你也可以注意到一个有趣的事情，&lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt; 的参数中只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint2&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;，这是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;controlPoint&lt;/code&gt; 完全是根据上一个点的控制点的镜像来计算得到的，无需传入。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt;（Smooth Quadratic Bezier Curve，平滑二次贝塞尔曲线）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：从上一个点开始，连一条平滑的&lt;strong&gt;二次贝塞尔曲线&lt;/strong&gt;到此命令的端点，确保在上一个点的曲线是连续的&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;T300,100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;与 &lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt; 一样，&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt; 也是确保在上一个点处平滑。控制点的计算方法也是一样对上一个点的控制点进行镜像。由于二次贝塞尔曲线只有一个控制点，所以它是无需传入控制点的，完全是算出来的。&lt;/p&gt;

&lt;h3 id=&quot;a-a-椭圆弧命令&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 椭圆弧命令&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;（Elliptical Arc，椭圆弧）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;含义：在上一个点和此命令的端点之间，连一条椭圆弧&lt;/li&gt;
  &lt;li&gt;参数：&lt;code class=&quot;highlighter-rouge&quot;&gt;size&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;rotationAngle&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;isLargeArcFlag&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;sweepDirectionFlag&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;endPoint&lt;/code&gt;（包含宽高两个值的尺寸 以度数计量的角度值 大于平角或小于平角标识 顺时针或逆时针标识 端点坐标）&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;A18.621,18.621,0,0,1,18.621,0.000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isLargeArcFlag&lt;/code&gt; 标识，如果角度大于 180° 则为 1，否则为 0；&lt;code class=&quot;highlighter-rouge&quot;&gt;sweepDirectionFlag&lt;/code&gt; 标识，如果顺时针则为 1，如果逆时针则为 0。&lt;/p&gt;

&lt;h3 id=&quot;z-z-闭合命令&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Z&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;z&lt;/code&gt; 闭合命令&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;含义：如果有此命令，那么图形将闭合形成填充区域；如果没有此命令，那么图形将只有线而不填充&lt;/li&gt;
  &lt;li&gt;没有参数&lt;/li&gt;
  &lt;li&gt;示例：&lt;code class=&quot;highlighter-rouge&quot;&gt;z&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此命令不区分大小写。&lt;/p&gt;

&lt;h2 id=&quot;解析&quot;&gt;解析&lt;/h2&gt;

&lt;p&gt;在这里挖一个坑，稍后贴出我用“访问者”模式编写的高性能高扩展性的路径语法解析代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/path-markup-syntax&quot;&gt;Path Markup Syntax - WPF .NET Framework - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/SVG/paths.html&quot;&gt;Paths — SVG 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/SVG11/paths.html&quot;&gt;Paths – SVG 1.1 (Second Edition)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 30 Jul 2021 00:50:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/path-markup-syntax.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/path-markup-syntax.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：创建一个简单的 exe 安装包</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;本文将带大家制作一个简单的 exe 安装包。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文开始前，请确保你已经可以生成一个最简单的 msi 安装包了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;用 WiX 制作安装包：创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于 exe 格式的安装包自己带了 UI，所以 msi 中的 UI 怎么样都是可以不用管的（不用纠结 msi 做成了扫什么样，能像上文结尾一样正常安装就好）。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建-wix-exe-项目&quot;&gt;创建 WiX EXE 项目&lt;/h2&gt;

&lt;p&gt;在解决方案上右键，“添加”-&amp;gt;“新建项目…”，然后在“添加新项目”窗口中搜索“WiX”，找到“Bootstrapper Project for WiX v3”。按“下一步”取个名字，然后“创建”。&lt;/p&gt;

&lt;p&gt;注意，选择的模板要注意这些要点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;图标上标记了“wix”，标签上标记了“WiX”&lt;/li&gt;
  &lt;li&gt;模板简介中说明这是在创建“EXE”文件&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-17-55-51.png&quot; alt=&quot;创建 WiX EXE 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-17-57-21.png&quot; alt=&quot;取个名字&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建完后，记得去项目属性里改一下输出的文件名。例如可以改成主项目的名称，也可以改成“XXX_Setup”这些大家喜欢用的名称。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-17-58-29.png&quot; alt=&quot;更改输出文件的名称&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;引用-msi-项目&quot;&gt;引用 MSI 项目&lt;/h2&gt;

&lt;p&gt;我们现在的这个项目生成的是捆绑包（Bundle），是为了将多个安装包集合到一起进行安装的。&lt;/p&gt;

&lt;p&gt;我们需要在这个捆绑包里面安装我们上一篇教程中创建的 MSI 安装包，所以我们需要引用这个创建 MSI 的项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-28-19.png&quot; alt=&quot;引用 MSI 项目&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;编辑-bundlewxs-文件&quot;&gt;编辑 Bundle.wxs 文件&lt;/h2&gt;

&lt;p&gt;在 Bundle.wxs 文件中，找到放 MSI 文件的注释处，将其替换成我们想安装的 MSI 文件。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Chain&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--    &amp;lt;!-- TODO: Define the list of chained packages. --&amp;gt;
--    &amp;lt;!-- &amp;lt;MsiPackage SourceFile=&quot;path\to\your.msi&quot; /&amp;gt; --&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;MsiPackage Compressed=&quot;yes&quot;
++                SourceFile=&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;/&amp;gt;
&lt;/span&gt;    &amp;lt;/Chain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这里的 Walterlv.Installer.Msi 是前一篇教程中引用的项目的名称，你可以改成你自己的生成 MSI 的项目的名称。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Compressed=&quot;yes&quot;&lt;/code&gt; 表示此 MSI 包会被嵌入到最终生成的 exe 文件中（反之则会松散地放到外部文件中）。可选值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;yes&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;no&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt;，对于 MSI 文件会默认嵌入，所以也可以不指定。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;编辑基本的安装包信息&quot;&gt;编辑基本的安装包信息&lt;/h3&gt;

&lt;p&gt;与 MSI 包一样，不填写基本的安装信息也会报编译错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-39-24.png&quot; alt=&quot;缺少厂商信息&quot; /&gt;&lt;br /&gt;
▲ 缺少厂商信息&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Product Id=&quot;*&quot;
&lt;span class=&quot;gd&quot;&gt;--           Name=&quot;Walterlv.Installer.Msi&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++           Name=&quot;Walterlv.Demo.MainApp&quot;
&lt;/span&gt;             Language=&quot;1033&quot;
             Version=&quot;1.0.0.0&quot;
&lt;span class=&quot;gd&quot;&gt;--           Manufacturer=&quot;&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++           Manufacturer=&quot;walterlv&quot;
&lt;/span&gt;             UpgradeCode=&quot;2aeffe1a-8bb6-4b06-b1c0-feca18e17cf7&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;Bundle Name=&quot;Walterlv.Installer.Exe&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;Bundle Name=&quot;Walterlv.Demo.MainApp&quot;
&lt;/span&gt;            Version=&quot;1.0.0.0&quot;
&lt;span class=&quot;gd&quot;&gt;--          Manufacturer=&quot;&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          Manufacturer=&quot;walterlv&quot;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--          UpgradeCode=&quot;528f80ca-a8f5-4bd4-8131-59fdcd69a411&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          UpgradeCode=&quot;2aeffe1a-8bb6-4b06-b1c0-feca18e17cf7&quot;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpgradeCode&lt;/code&gt; 如果改成和之前的 MSI 文件的一样，那么无论是做成 MSI 还是 EXE 格式的安装包，他们都是可以互相被升级的。&lt;/p&gt;

&lt;p&gt;当然，对于一个 Bundle 来说可以集合多个安装包。当要一次安装多个 MSI 包的时候，建议选不一样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpgradeCode&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;关于设置 MSI 和 EXE 安装包的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpgradeCode&lt;/code&gt; 的更多细节，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-toolset-should-the-upgrade-code-be-the-same-between-exe-and-msi&quot;&gt;MSI 和 EXE 的 UpgradeCode 应该设置成相同还是不同？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;测试效果&quot;&gt;测试效果&lt;/h2&gt;

&lt;p&gt;现在，我们完成了一个最简单的 EXE 安装包，测试安装一下。&lt;/p&gt;

&lt;p&gt;前往 EXE 文件的输出目录（在项目目录的 bin\Debug 下）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-47-40.png&quot; alt=&quot;前往 EXE 文件的输出目录&quot; /&gt;&lt;br /&gt;
▲ 前往 EXE 文件的输出目录&lt;/p&gt;

&lt;p&gt;双击安装，可以出现默认的安装界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-49-29.png&quot; alt=&quot;默认的安装界面&quot; /&gt;&lt;br /&gt;
▲ 默认的安装界面&lt;/p&gt;

&lt;p&gt;安装完后，可以在系统设置“应用和功能”以及“Program Files”目录中找到它：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-50-47.png&quot; alt=&quot;系统设置应用和功能&quot; /&gt;&lt;br /&gt;
▲ 系统设置应用和功能&lt;/p&gt;

&lt;p&gt;测试完成后，记得及时卸载掉这个包。虽然这次没什么影响，但后续我们会学到的某个操作可能导致未及时卸载的包再也无法通过正常途径卸载，所以请保持良好的习惯。（虚拟机调试的小伙伴可无视）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-51-47.png&quot; alt=&quot;卸载包&quot; /&gt;&lt;br /&gt;
▲ 卸载包&lt;/p&gt;

&lt;p&gt;另外，觉得不错可以提交一下代码，方便后续章节的学习。&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;附上必要的源码，避免你在阅读教程时因模板文件的版本差异造成一些意料之外的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-18-52-22.png&quot; alt=&quot;必要的源码&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;bundlewxs&quot;&gt;Bundle.wxs&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;// 除了本文所说的改动外，本文件的其他内容均保持模板文件的原始模样。&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Bundle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainApp&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Manufacturer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;UpgradeCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;528f80ca-a8f5-4bd4-8131-59fdcd69a411&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BootstrapperApplicationRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WixStandardBootstrapperApplication.RtfLicense&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Chain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;MsiPackage&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Compressed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Chain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Bundle&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 20 Jul 2021 01:11:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-exe-hello-world.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-exe-hello-world.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>0x800b0109 - 已处理证书链，但是在不受信任提供程序信任的根证书中终止。</title>
        <description>&lt;p&gt;有时在安装程序时无法安装出现错误，或者在更新某些系统组件时也遇到同样的错误：“已处理证书链，但是在不受信任提供程序信任的根证书中终止。”。&lt;/p&gt;

&lt;p&gt;本文介绍其原因和解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;错误&quot;&gt;错误&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-17-41-15.png&quot; alt=&quot;错误提示 - 来自 .NET Framework&quot; /&gt;&lt;br /&gt;
▲ 错误提示 - 来自 .NET Framework&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-17-34-52.png&quot; alt=&quot;错误提示 - 来自 WiX 入门教程&quot; /&gt;&lt;br /&gt;
▲ 错误提示 - 来自&lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX 入门教程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;错误码：&lt;code class=&quot;highlighter-rouge&quot;&gt;0x800b0109&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;已处理证书链，但是在不受信任提供程序信任的根证书中终止。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/hresult-in-windows.html&quot;&gt;使用 err 工具来查询此错误码&lt;/a&gt; 也能得到相同的提示：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;❯&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;800b0109&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# for hex 0x800b0109 / decimal -2146762487&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CERT_E_UNTRUSTEDROOT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;winerror.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# A certificate chain processed, but terminated in a root&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# certificate which is not trusted by the trust provider.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 1 matches found for &quot;800b0109&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;操作系统中不含此 .NET Framework 版本需要验证的在有效时间内的微软根证书（Microsoft Root Certificate Authority 2011）。&lt;/p&gt;

&lt;p&gt;目前已知最新版的 Windows 7 SP1 (x86) 系统在未安装系统所需补丁的情况下不带此证书，而 Windows 7 SP1 (x64) 系统的最新版带有此证书。其他更(gèng)新的 Windows 8、Windows 10 全系都带有此证书。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;为系统安装有效的微软证书即可。&lt;/p&gt;

&lt;p&gt;第一步：&lt;a href=&quot;/static/attachments/MicrosoftRootCertificateAuthority2011.zip&quot;&gt;下载证书 MicrosoftRootCertificateAuthority2011.zip&lt;/a&gt;，下载完后解压得到 MicrosoftRootCertificateAuthority2011.cer 文件。（你也可以从其他已安装证书的电脑上导出。）&lt;/p&gt;

&lt;p&gt;第二步：双击安装证书。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;点击“安装证书”，下一步；&lt;/li&gt;
  &lt;li&gt;选择“将所有的证书放入下列存储”，然后选择“浏览…”；&lt;/li&gt;
  &lt;li&gt;选择“受信任的证书办法机构”，然后选择“下一步”；&lt;/li&gt;
  &lt;li&gt;在“安全性警告”中，点击“是”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-17-44-14.png&quot; alt=&quot;双击证书文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-17-44-23.png&quot; alt=&quot;打开导入向导&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-17-44-29.png&quot; alt=&quot;选择存储位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后，重新安装 .NET Framework 或者其他程序即可。&lt;/p&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 09:51:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/terminated-in-a-root-certificate-which-is-not-trusted.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/terminated-in-a-root-certificate-which-is-not-trusted.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>WiX Toolset 安装包制作入门教程（目录篇）</title>
        <description>&lt;p&gt;WiX 全称为 Windows Installer XML，是使用 XML 文件创建 Windows 安装程序的一组工具集。它开源且完全免费。&lt;/p&gt;

&lt;p&gt;虽然这一组工具集功能非常强大，但学习曲线较陡峭，在没有人指导的情况下独立完成完整的安装包制作会比较困难。对安装包技术零基础的开发者，甚至仅凭阅读官方文档的教程系列也难以完成 Hello World 级别的安装包制作。所以本系列博客的出现旨在填补官方教程系列的这一空缺，希望零基础的开发者也能在本教程的帮助下独立完成整套安装包的制作。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系列教程说明&quot;&gt;系列教程说明&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;截至 2021 年 7 月，WiX 4 尚处在预览阶段，所以本系列教程基于 WiX 3 进行。&lt;/li&gt;
  &lt;li&gt;本系列教程所需的所有源代码都已在 GitHub 上开源，你可以克隆下来学习和试验，也可以选择性忽略。&lt;/li&gt;
  &lt;li&gt;如果你在阅读教程时发现有些步骤不对（或者按步骤完成后依然无法跑通，或者遇到了各种奇葩问题），欢迎在评论区留言，或加我的 QQ 交流（450711383）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;分类-hello-world&quot;&gt;分类 Hello World&lt;/h2&gt;

&lt;p&gt;WiX 能制作不同种类的安装包，各类安装包的制作方法不同，做 Hello World 所需的步骤也不一样。所以这里分一下类，每个类别都可从零开始完成整个类别的 Hello World。&lt;/p&gt;

&lt;p&gt;你可以挑自己想做的安装包类型，然后直接在这个类别里面从第一篇读至最后一篇。&lt;/p&gt;

&lt;h3 id=&quot;msi-格式安装包的-hello-world&quot;&gt;msi 格式安装包的 Hello World&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-build-tools&quot;&gt;安装 WiX Toolset 工具集&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-visual-studio-extensions&quot;&gt;安装 WiX Toolset Visual Studio 插件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution&quot;&gt;准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;使用 WiX 创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;exe-格式安装包的-hello-world&quot;&gt;exe 格式安装包的 Hello World&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-build-tools&quot;&gt;安装 WiX Toolset 工具集&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-visual-studio-extensions&quot;&gt;安装 WiX Toolset Visual Studio 插件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution&quot;&gt;准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;使用 WiX 创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-exe-hello-world&quot;&gt;使用 WiX 创建一个简单的 exe 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;要求-net-framework-前置的-hello-world&quot;&gt;要求 .NET Framework 前置的 Hello World&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-build-tools&quot;&gt;安装 WiX Toolset 工具集&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-visual-studio-extensions&quot;&gt;安装 WiX Toolset Visual Studio 插件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution&quot;&gt;准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;使用 WiX 创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-detect-net-framework&quot;&gt;为 WiX 制作的 msi 安装包添加 .NET Framework 环境检查&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-exe-hello-world&quot;&gt;使用 WiX 创建一个简单的 exe 安装包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-bundle-detect-and-install-net-framework&quot;&gt;为 WiX 制作的 exe 安装包添加 .NET Framework 前置的安装步骤&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;使用-wpf-制作安装界面的-hello-world&quot;&gt;使用 WPF 制作安装界面的 Hello World&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-build-tools&quot;&gt;安装 WiX Toolset 工具集&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-visual-studio-extensions&quot;&gt;安装 WiX Toolset Visual Studio 插件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution&quot;&gt;准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;使用 WiX 创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-exe-hello-world&quot;&gt;使用 WiX 创建一个简单的 exe 安装包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-bundle-detect-and-install-net-framework&quot;&gt;为 WiX 制作的 exe 安装包添加 .NET Framework 前置的安装步骤&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;使用 WPF 制作安装界面（入门篇）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;你可能在-hello-world-系列中遇到的问题和解决办法汇总&quot;&gt;你可能在 Hello World 系列中遇到的问题和解决办法汇总&lt;/h2&gt;

&lt;h3 id=&quot;方法与汇总&quot;&gt;方法与汇总&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-the-pit-you-might-step-on&quot;&gt;使用 WiX 创建最简单的安装包过程中可能出现的问题和解决方案汇总&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-view-wix-burn-installer-logs&quot;&gt;如何查看用 WiX 制作的安装包的日志&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-debug-wix-burn-installer&quot;&gt;如何调试用 WiX 制作的安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;具体问题&quot;&gt;具体问题&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-burn-always-install-netfx-even-if-already-installed&quot;&gt;用 WiX 制作安装包：设置的 .NET Framework 前置会始终安装，即使目标电脑已经自带或装好&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-managed-bootstrapper-application-error-80070002&quot;&gt;用 WiX Burn 制作托管安装包：出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80070002&lt;/code&gt; 错误&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-managed-bootstrapper-application-error-80131508&quot;&gt;用 WiX Burn 制作托管安装包：出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80131508&lt;/code&gt; 错误&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/terminated-in-a-root-certificate-which-is-not-trusted&quot;&gt;0x800b0109 - 已处理证书链，但是在不受信任提供程序信任的根证书中终止。&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;基本概念和原则&quot;&gt;基本概念和原则&lt;/h2&gt;

&lt;p&gt;在完成了前面的 Hello World 系列教程后，你需要跑完整个流程才算真正做了一个安装包。然而，由于 WiX 本身的入门并不容易，你可能需要了解一些基本的概念才能更容易地完成整个安装流程。&lt;/p&gt;

&lt;p&gt;不用担心，这里只会涉及到完成最简流程需要用到的那些概念，更深入的概念我会在其他系列的教程里再说明。&lt;/p&gt;

&lt;p&gt;// 未完待续…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-installer-using-wix-best-practice-product-id-and-upgrade-code&quot;&gt;WiX 安装包制作最佳实践：Id、UpgradeCode 应该怎么设置？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;完成主要安装流程&quot;&gt;完成主要安装流程&lt;/h2&gt;

&lt;p&gt;// 未完待续…&lt;/p&gt;

&lt;h2 id=&quot;可供查阅的资料汇总&quot;&gt;可供查阅的资料汇总&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;&quot;&gt;可在 wxs 中编写的项目引用变量 $(var.ProjectName.Xxx) 系列)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;其他-wix-toolset-教程系列&quot;&gt;其他 WiX Toolset 教程系列&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;&quot;&gt;WiX Toolset 安装包制作中级教程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wixtoolset.org/documentation/manual/v3/&quot;&gt;WiX Toolset v3 Manual Table of Contents&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/huaxia283611/p/WiX-ToolsetIndex.html&quot;&gt;WiX Toolset 教程索引页 - 奇葩史 - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/14863905/wix-bundle-exepackage-detectcondition-is-always-false&quot;&gt;visual studio 2010 - WiX ‘Bundle’ ‘ExePackage’ ‘DetectCondition’ is always false - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 09:48:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>如何调试 WiX Burn 制作的自定义托管引导程序的 exe 安装包</title>
        <description>&lt;p&gt;WiX 本身很强大，使用本来也没那么难。奈何 WiX 3 的官方文档可读性极差且长期不更新，于是新手在使用 WiX 制作安装包时极容易出问题，导致制作的安装包各种行为不正常。&lt;/p&gt;

&lt;p&gt;虽然我写了一系列的 WiX 安装包入门教程来帮助大家避坑，还写了一些常见问题的解决方法，但大家遇到的问题总会比我整理的要多。所以教大家&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;查看日志&quot;&gt;查看日志&lt;/h2&gt;

&lt;p&gt;很多时候，看日志能帮助你快速找到原因。以下是查看日志的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-view-wix-burn-installer-logs.html&quot;&gt;如何查看用 WiX 制作的安装包的日志&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;debuggerlaunch&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch()&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;如果安装过程能执行到你编写的 C# 代码中，那么可以在入口处加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch()&lt;/code&gt; 来启动调试器。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class Program : BootstrapperApplication
    {
        protected override void Run()
        {
&lt;span class=&quot;gi&quot;&gt;++          if (Environment.GetCommandLineArgs().Contains(&quot;-debug&quot;, StringComparer.OrdinalIgnoreCase))
++          {
++              Debugger.Launch();
++          }
&lt;/span&gt;
            // 省略其他启动代码。
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我加上了一个命令行参数的判断，即如果启动安装包 exe 的时候带上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;-debug&lt;/code&gt; 参数，那么就启动调试器。（我用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; 而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;--&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 的原因是 burn 引擎用的就是单个短线。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-16-41-05.png&quot; alt=&quot;选择调试器&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;对比测试&quot;&gt;对比测试&lt;/h2&gt;

&lt;p&gt;如果出现的问题日志上说明不明显，代码也没执行到自定义引导程序部分，那么可以考虑对照正常状态的 WiX 项目替换组件调查。这可以快速将问题范围定位到某个文件甚至是某行代码上。&lt;/p&gt;

&lt;p&gt;例如在&lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui.html&quot;&gt;制作 WPF 安装包界面的教程&lt;/a&gt;中，我们有四个项目。这个示例&lt;a href=&quot;https://github.com/walterlv/Walterlv.WixInstallerDemo/tree/1b6600bb694c593894fc20cea76154b61ccf0c1f&quot;&gt;已经开源到 GitHub 上了&lt;/a&gt;。于是我们可以尝试将出问题的项目中的部分模块替换成这个正常的项目对应部分。当最终能正常工作时，最近替换的模块便最有可能是问题模块。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-23-19.png&quot; alt=&quot;制作 WPF 安装包界面教程中的项目&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 08:47:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-debug-wix-burn-installer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-debug-wix-burn-installer.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX Burn 制作托管安装包：出现 0x80131508 错误</title>
        <description>&lt;p&gt;使用 WiX 的 Burn 引擎制作自定义托管引导程序的 exe 安装包时，双击生成的安装包没有反应。如果查看日志可以发现有 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80131508&lt;/code&gt; 错误。本文介绍其调查和解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;现象&quot;&gt;现象&lt;/h2&gt;

&lt;p&gt;双击制作的自定义引导程序的 exe 安装包没有反应，通过&lt;a href=&quot;https://blog.walterlv.com/post/how-to-view-wix-burn-installer-logs.html&quot;&gt;查看 Burn 引擎的输出日志&lt;/a&gt;可以发现如下关键的错误码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
[BCD8:B4DC][2021-07-16T11:47:32]i000: Loading managed bootstrapper application.
[BCD8:B4DC][2021-07-16T11:47:32]e000: Error 0x80131508: Failed to create the managed bootstrapper application.
[BCD8:B4DC][2021-07-16T11:47:32]e000: Error 0x80131508: Failed to create UX.
[BCD8:B4DC][2021-07-16T11:47:32]e000: Error 0x80131508: Failed to load UX.
[BCD8:B4DC][2021-07-16T11:47:32]e000: Error 0x80131508: Failed while running 
...
[BCD8:B4DC][2021-07-16T11:47:32]e000: Error 0x80131508: Failed to run per-user mode.
[BCD8:B4DC][2021-07-16T11:47:32]i007: Exit code: 0x80131508, restarting: No
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;调查&quot;&gt;调查&lt;/h2&gt;

&lt;p&gt;通过&lt;a href=&quot;https://blog.walterlv.com/post/hresult-in-windows.html&quot;&gt;查询 HRESULT 错误码&lt;/a&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80131508&lt;/code&gt; 可以得知它代表的意思是“INDEXOUTOFRANGE”。啊这……说明是 Burn 引擎出现了内部因为某些原因出现了错误，并且没有正确把错误原因标记出来。&lt;/p&gt;

&lt;p&gt;然而对我们简单的托管安装包界面来说，更可能是我们自己的某些配置或代码不正确，导致 Burn 引擎内部代码炸掉的。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;这样的错误几乎不具有可调试性。因此，我直接将我偶然发现的原因和解决办法贴出来。&lt;/p&gt;

&lt;p&gt;参考&lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;这篇入门教程&lt;/a&gt;中的代码，如果 AssemblyInfo.cs 文件中缺少标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperApplication&lt;/code&gt; 类型的特性，那么就会出现此错误。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
++  using Microsoft.Tools.WindowsInstallerXml.Bootstrapper;

++  using Walterlv.InstallerUI;

++  [assembly: BootstrapperApplication(typeof(Program))]
&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而呀，官方在教大家写托管引导程序的时候，翻遍了整个文档都没有提醒过要写这个特性！所以特别容易被官方文档带偏，这里记录此文章避免大家踩坑。&lt;/p&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 06:24:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wix-managed-bootstrapper-application-error-80131508.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wix-managed-bootstrapper-application-error-80131508.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：设置的 .NET Framework 前置会始终安装，即使目标电脑已经自带或装好</title>
        <description>&lt;p&gt;使用 WiX 的 Burn 引擎制作自定义托管引导程序的 exe 安装包时，你可能会遇到这种情况：明明目标电脑上已经装好了 .NET Framework，但无论如何就是会提示安装，始终不启动自定义的安装界面。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;现象&quot;&gt;现象&lt;/h2&gt;

&lt;p&gt;即使是在开发机上（.NET Framework 已经装好），双击制作的 exe 安装包也依然会提示安装 .NET Framework：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-16-11-04-09.png&quot; alt=&quot;提示安装 .NET Framework&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果强行安装，装完也依然不会启动自定义的引导程序。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;这个错误其实非常有误导性！&lt;/p&gt;

  &lt;p&gt;看起来不断提示要安装 .NET Framework，会让人误以为是 .NET Framework 的安装判断条件写出了问题，然后朝着 Product.wxs 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Condition&lt;/code&gt;、Bundle.wxs 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;InstallCondition&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;DetectCondition&lt;/code&gt; 去调查。然而这是捆绑包中的判断，与 Product.wxs 无关；我们默认用的是 WixNetFxExtension.dll 中的判断，这很靠谱，也不会出问题，所以也与 &lt;code class=&quot;highlighter-rouge&quot;&gt;InstallCondition&lt;/code&gt; 和&lt;code class=&quot;highlighter-rouge&quot;&gt;DetectCondition&lt;/code&gt; 无关。&lt;/p&gt;

  &lt;p&gt;正确的调查方法是去看错误日志，看真实的错误原因是什么。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当停留在这个“安装 .NET Framework”的界面时，&lt;a href=&quot;https://blog.walterlv.com/post/how-to-view-wix-burn-installer-logs.html&quot;&gt;查看 Burn 引擎的输出日志&lt;/a&gt;：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[14A4:9F04][2021-07-16T11:13:57]i001: Burn v3.11.2.4516, Windows v10.0 (Build 22000: Service Pack 0), path: C:\Users\lvyi\AppData\Local\Temp\{4310ECA0-6A28-4BE6-922D-570EAA81C0CD}\.cr\Walterlv.Demo.MainApp.exe
[14A4:9F04][2021-07-16T11:13:57]i009: Command Line: '-burn.clean.room=D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe -burn.filehandle.attached=464 -burn.filehandle.self=460 -l debug.log'
[14A4:9F04][2021-07-16T11:13:57]i000: Setting string variable 'WixBundleOriginalSource' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe'
[14A4:9F04][2021-07-16T11:13:57]i000: Setting string variable 'WixBundleOriginalSourceFolder' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\'
[14A4:9F04][2021-07-16T11:13:57]i000: Setting string variable 'WixBundleLog' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\debug.log'
[14A4:9F04][2021-07-16T11:13:57]i000: Setting string variable 'WixBundleName' to value 'Walterlv.Demo.MainApp'
[14A4:9F04][2021-07-16T11:13:57]i000: Setting string variable 'WixBundleManufacturer' to value 'walterlv'
[14A4:9F04][2021-07-16T11:13:57]i000: Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x80070490.
[14A4:A444][2021-07-16T11:13:58]i000: Setting numeric variable 'WixStdBALanguageId' to value 2052
[14A4:A444][2021-07-16T11:13:58]i000: Setting version variable 'WixBundleFileVersion' to value '1.0.0.0'
[14A4:9F04][2021-07-16T11:13:58]i100: Detect begin, 2 packages
[14A4:9F04][2021-07-16T11:13:58]i000: Setting string variable 'NETFRAMEWORK45' to value '528449'
[14A4:9F04][2021-07-16T11:13:58]i052: Condition 'NETFRAMEWORK45 &amp;gt;= 394802' evaluates to true.
[14A4:9F04][2021-07-16T11:13:58]i101: Detected package: NetFx462Web, state: Present, cached: None
[14A4:9F04][2021-07-16T11:13:58]i101: Detected package: Walterlv.Demo.MainApp.msi, state: Absent, cached: None
[14A4:9F04][2021-07-16T11:13:58]i199: Detect complete, result: 0x0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;调查&quot;&gt;调查&lt;/h2&gt;

&lt;p&gt;可以注意到唯一的一行错误：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x80070490.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;加载安装前置的引导程序，因为托管宿主无法被加载，错误代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80070490&lt;/code&gt;。所以导致弹出 .NET Framework 安装界面的原因是&lt;strong&gt;引导程序无法加载我们的自定义界面，误认为前置没有装好，所以弹出了前置安装界面&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/hresult-in-windows.html&quot;&gt;查询一下错误码&lt;/a&gt;：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;❯&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;80070490&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# No results found for hex 0x4c5c75a / decimal 80070490&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# for hex 0x80070490 / decimal -2147023728&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PEER_E_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                               &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p2p.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;E_PROP_ID_UNSUPPORTED&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;vfwmsgs.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# The specified property ID is not supported for the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# specified property set.%0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WER_E_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;werapi.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DRM_E_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;windowsplayready.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# as an HRESULT: Severity: FAILURE (1), FACILITY_WIN32 (0x7), Code 0x490&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# for hex 0x490 / decimal 1168&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ERROR_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;winerror.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Element not found.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 5 matches found for &quot;80070490&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“Not Found”，并不能给我们带来什么有价值的信息。&lt;/p&gt;

&lt;p&gt;虽然错误码无法给我们带来有价值的信息，但那句提示至少可以让我们知道问题出在“无法加载托管宿主”这个范围。&lt;/p&gt;

&lt;p&gt;这可能是两个范围：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperApplication&lt;/code&gt; 的第一行代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 之&lt;strong&gt;前&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;我们自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperApplication&lt;/code&gt; 的第一行代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 之&lt;strong&gt;后&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这很好区分，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 的第一句加上一个 “Debugger.Launch()”，看看再启动安装包的时候是否会弹出调试器选择框即可。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    protected override void Run()
    {
&lt;span class=&quot;gi&quot;&gt;++      Debugger.Launch();
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们就这样试一下，可以发现不会弹出调试器选择框。所以以上的两个范围只能是范围 1。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;实际上按目前的日志输出，已经足以确定是范围 1 了，不过这需要一些先验知识，即托管引导程序能捕获 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法中的所有异常。也就是说无论你的代码怎么写，托管引导程序都能把你引导起来，而不会出现此日志中输出的那样“无法加载托管宿主”。前面这个调查模拟没有此先验知识的情况，你可以从中学习到更多的 Burn MBA（Managed Bootstrapper Application）调试技巧。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;有哪些东西会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 之前？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;BootstrapperCore.config 文件的配置&lt;/li&gt;
  &lt;li&gt;程序集元数据&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于 1，如果你能看出来 BootstrapperCore.config 配置出现了哪些问题更好，看不出来的话可以把现成的例子拿出来对比（例如&lt;a href=&quot;https://github.com/walterlv/Walterlv.WixInstallerDemo/tree/1b6600bb694c593894fc20cea76154b61ccf0c1f&quot;&gt;我在入门教程里写的 DEMO 程序&lt;/a&gt;，记得要改项目名）。确保里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;assemblyName&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;supportedRuntime&lt;/code&gt; 属性赋值正确（可参见&lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;我入门教程中写的配置和可用值说明&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;对于 2，通过实验可以得知程序集元数据出现错误时的错误码不是这个（参见：&lt;a href=&quot;/post/wix-managed-bootstrapper-application-error-80131508&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;0x80131508&lt;/code&gt; 错误&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;于是我们也能得出问题出在 BootstrapperCore.config 配置里。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;可按下面的配置作为参考，将你的配置改到正确（参见我的 WiX 入门教程）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;sectionGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wix.bootstrapper&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;host&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/sectionGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;wix.bootstrapper&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;host&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;assemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.InstallerUI&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedFramework&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4\Full&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/host&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/wix.bootstrapper&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外多说一句，官方文档对 BootstrapperCore.config 的描述非常具有误导性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/install_dotnet.html&quot;&gt;How To: Install the .NET Framework Using Burn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.shisujie.com/blog/Install-the-dotNet-Framework-Using-Burn&quot;&gt;奇葩史的奇葩事 - [译]：WiX Toolset使用技巧——使用Burn引擎安装.NET Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;官方文档示例的注释中要大家改 &lt;code class=&quot;highlighter-rouge&quot;&gt;host/@assemblyName&lt;/code&gt;，但实际上按官方文档的改法改好了就会出现本文所述的错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/46322994/6233938&quot;&gt;installation - Wix ExePackage always installs regardless of DetectCondition, InstallCondition, on install, or uninstall - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 04:08:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wix-burn-always-install-netfx-even-if-already-installed.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wix-burn-always-install-netfx-even-if-already-installed.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>使用 WiX 创建最简单的安装包过程中可能出现的问题和解决方案汇总</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的番外篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;用 WiX 制作安装包还是有些门槛的。如果你没有完全按照我教程中提供的步骤来执行（例如你用了自己的项目名，却在复制关键代码时没有改成自己的），那么极有可能在最终生成安装包后无法运行。&lt;/p&gt;

&lt;p&gt;本文记录一些跟着教程做时可能遇到的常见问题，帮助你在遇到问题后能及时找到解决方案。如果看完还没有解决你的问题，欢迎留言探讨，也可以尝试 &lt;a href=&quot;/post/how-to-debug-wix-burn-installer&quot;&gt;调试 WiX 制作的安装包&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;无法启动-exe-安装包&quot;&gt;无法启动 exe 安装包&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-managed-bootstrapper-application-error-80070002&quot;&gt;用 WiX Burn 制作托管安装包：出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80070002&lt;/code&gt; 错误&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-managed-bootstrapper-application-error-80131508&quot;&gt;用 WiX Burn 制作托管安装包：出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80131508&lt;/code&gt; 错误&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;net-framework-始终会安装&quot;&gt;.NET Framework 始终会安装&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wix-burn-always-install-netfx-even-if-already-installed&quot;&gt;用 WiX 制作安装包：设置的 .NET Framework 前置会始终安装，即使目标电脑已经自带或装好&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 03:50:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-the-pit-you-might-step-on.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-the-pit-you-might-step-on.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX Burn 制作托管安装包：出现 0x80070002 错误</title>
        <description>&lt;p&gt;使用 WiX 的 Burn 引擎制作自定义托管引导程序的 exe 安装包时，双击生成的安装包没有反应。如果查看日志可以发现有 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80070002&lt;/code&gt; 错误。本文介绍其调查和解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;现象&quot;&gt;现象&lt;/h2&gt;

&lt;p&gt;双击制作的自定义引导程序的 exe 安装包没有反应，通过&lt;a href=&quot;https://blog.walterlv.com/post/how-to-view-wix-burn-installer-logs.html&quot;&gt;查看 Burn 引擎的输出日志&lt;/a&gt;可以发现如下关键的错误码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
[1874:8D8C][2021-07-15T19:28:29]i000: Loading managed bootstrapper application.
[1874:8D8C][2021-07-15T19:28:29]e000: Error 0x80070002: Failed to create the managed bootstrapper application.
[1874:8D8C][2021-07-15T19:28:29]e000: Error 0x80070002: Failed to create UX.
[1874:8D8C][2021-07-15T19:28:29]e000: Error 0x80070002: Failed to load UX.
[1874:8D8C][2021-07-15T19:28:29]e000: Error 0x80070002: Failed while running 
...
[1874:8D8C][2021-07-15T19:28:29]e000: Error 0x80070002: Failed to run per-user mode.
[1874:8D8C][2021-07-15T19:28:29]i007: Exit code: 0x80070002, restarting: No
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;调查&quot;&gt;调查&lt;/h2&gt;

&lt;p&gt;通过&lt;a href=&quot;https://blog.walterlv.com/post/hresult-in-windows.html&quot;&gt;查询 HRESULT 错误码&lt;/a&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;0x80070002&lt;/code&gt; 可以得知它代表的意思是“文件不存在”。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;❯&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0x80070002&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# for hex 0x80070002 / decimal -2147024894&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COR_E_FILENOTFOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;corerror.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DIERR_NOTFOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dinput.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DIERR_OBJECTNOTFOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dinput.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;STIERR_OBJECTNOTFOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stierr.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DRM_E_WIN32_FILE_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                     &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;windowsplayready.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;E_FILE_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                               &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wpc.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# as an HRESULT: Severity: FAILURE (1), FACILITY_NTWIN32 (0x7), Code 0x2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# for hex 0x2 / decimal 2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;STATUS_WAIT_2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                                  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ntstatus.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# as an HRESULT: Severity: FAILURE (1), FACILITY_WIN32 (0x7), Code 0x2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ERROR_FILE_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;winerror.h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# The system cannot find the file specified.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 8 matches found for &quot;0x80070002&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以到底是什么不存在呢？&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;这篇入门教程&lt;/a&gt;中，涉及到找不到托管引导程序（WiX 官方喜欢称之为 MBA，Managed Bootstrapper Application）的地方可能有这些：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Bundle.wxs 文件中将托管引导程序加入到负载的地方&lt;/li&gt;
  &lt;li&gt;BootstrapperCore.config 文件中，设置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;assemblyName&lt;/code&gt; 属性&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于 1，如果加入到负载时文件不存在，那么这个 Bundle.wxs 所在的项目都无法编译通过，所以 1 原因可以排除。那么只剩下原因 2 了，如果发现其名称与实际程序集名称不一样（例如改了项目名，或者从教程中复制了代码却没有对应改成自己项目中的名字），那么原因就是这个了。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;修改 BootstrapperCore.config 文件（在&lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;这篇教程&lt;/a&gt;中是 App.config 文件），将 &lt;code class=&quot;highlighter-rouge&quot;&gt;assemblyName&lt;/code&gt; 的值改为正确的托管引导程序（MBA）的名字。&lt;/p&gt;
</description>
        <pubDate>Fri, 16 Jul 2021 02:48:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wix-managed-bootstrapper-application-error-80070002.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wix-managed-bootstrapper-application-error-80070002.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>如何查看用 WiX 制作的安装包的日志</title>
        <description>&lt;p&gt;如果你使用 WiX 制作安装包，并且遇到了问题，一定需要一个趁手的调试方案。本文介绍如何查看 WiX 制作的安装包的日志。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;WiX 使用 Burn 引擎来制作 exe 捆绑包，默认情况下 Burn 引擎使用自带的安装界面来执行安装。Burn 引擎提供了自定义引导程序的功能，于是你可以利用 Burn 引擎做出自己的 UI 来。比如 &lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui&quot;&gt;用 WPF 来制作安装包界面&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;因此，我们有通用的方法来查看安装日志，只需要在启动安装程序时传入参数：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Walterlv.Demo.MainApp.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;debug.log&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.MainApp.exe&lt;/code&gt; 是我在 WiX 入门教程系列中使用的安装包名。&lt;code class=&quot;highlighter-rouge&quot;&gt;-l &quot;debug.log&quot;&lt;/code&gt; 表示在当前目录下使用“debug.log”文件记录日志。&lt;/p&gt;

&lt;p&gt;以下是一个成功运行的自定义捆绑包的日志：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[476C:2F20][2021-07-15T19:35:43]i001: Burn v3.11.2.4516, Windows v10.0 (Build 22000: Service Pack 0), path: C:\Users\lvyi\AppData\Local\Temp\{AC43FA57-2BC7-45FA-8A6C-B8ADABB376C7}\.cr\Walterlv.Demo.MainApp.exe
[476C:2F20][2021-07-15T19:35:44]i009: Command Line: '-burn.clean.room=D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe -burn.filehandle.attached=684 -burn.filehandle.self=692 -l debug.log'
[476C:2F20][2021-07-15T19:35:44]i000: Setting string variable 'WixBundleOriginalSource' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe'
[476C:2F20][2021-07-15T19:35:44]i000: Setting string variable 'WixBundleOriginalSourceFolder' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\'
[476C:2F20][2021-07-15T19:35:44]i000: Setting string variable 'WixBundleLog' to value 'D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\debug.log'
[476C:2F20][2021-07-15T19:35:44]i000: Setting string variable 'WixBundleName' to value 'Walterlv.Demo.MainApp'
[476C:2F20][2021-07-15T19:35:44]i000: Setting string variable 'WixBundleManufacturer' to value 'walterlv'
[476C:2F20][2021-07-15T19:35:44]i000: Loading managed bootstrapper application.
[476C:2F20][2021-07-15T19:35:44]i000: Creating BA thread to run asynchronously.
[476C:A250][2021-07-15T19:35:44]i000: Running the Walterlv.InstallerUI.
[476C:A250][2021-07-15T19:35:59]i000: Exiting the Walterlv.InstallerUI.
[476C:A250][2021-07-15T19:35:59]i000: The Walterlv.InstallerUI has exited.
[476C:2F20][2021-07-15T19:35:59]i500: Shutting down, exit code: 0x0
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleAction = 5
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleElevated = 0
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleLog = D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\debug.log
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleManufacturer = walterlv
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleName = Walterlv.Demo.MainApp
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleOriginalSource = D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleOriginalSourceFolder = D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleProviderKey = {87942d31-3870-42ca-8100-4e381772c7d6}
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleSourceProcessFolder = D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleSourceProcessPath = D:\WIP\Desktop\Walterlv.WixInstallerDemo\Walterlv.Installer.Exe\bin\Debug\Walterlv.Demo.MainApp.exe
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleTag = 
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleUILevel = 4
[476C:2F20][2021-07-15T19:35:59]i410: Variable: WixBundleVersion = 1.0.0.0
[476C:2F20][2021-07-15T19:35:59]i007: Exit code: 0x0, restarting: No
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 12:18:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-view-wix-burn-installer-logs.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-view-wix-burn-installer-logs.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：制作 WPF 安装包界面（入门篇）</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;前面我们制作完成了一个可自动安装 .NET Framework 依赖的 exe 安装包，下面我们将学习制作自己的安装界面。本文使用 WPF 制作安装包界面。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;本文准备做什么&quot;&gt;本文准备做什么？&lt;/h2&gt;

&lt;p&gt;本文将继续在&lt;a href=&quot;/post/getting-started-with-wix-toolset-bundle-detect-and-install-net-framework&quot;&gt;前一篇文章&lt;/a&gt;的解决方案基础上继续学习。&lt;/p&gt;

&lt;p&gt;我们将做这些事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建一个 WPF 项目，用来做安装包的 UI&lt;/li&gt;
  &lt;li&gt;处理一下这个 WPF 项目，使其对接 WiX 的捆绑包&lt;/li&gt;
  &lt;li&gt;修改用来生成 exe 格式安装包的捆绑包项目，使其接入 WPF UI 项目&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;创建-wpf-项目&quot;&gt;创建 WPF 项目&lt;/h2&gt;

&lt;p&gt;为我们的解决方案新建一个 WPF 项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-12-53.png&quot; alt=&quot;新建一个 WPF 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建好后，我们需要双击这个 WPF 项目名，以编辑其项目文件，把框架改为 .NET Framework。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
&lt;/span&gt;
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;WinExe&amp;lt;/OutputType&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;TargetFramework&amp;gt;net5.0-windows&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;TargetFramework&amp;gt;net462&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;        &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-23-19.png&quot; alt=&quot;新建好的 WPF 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了后面能适配 WiX 捆绑包，我们先建一个 Program.cs 文件作为启动文件。（名字随便取，我用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 只是为了体现出它是一个入口。）&lt;/p&gt;

&lt;p&gt;在 Program.cs 里写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 方法替代 App.xaml 自动生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.InstallerUI&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这里的代码仅为调试使用，在最终的项目中无任何用途。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，设置此 WPF UI 项目的属性，将启动对象修改成我们新建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-27-01.png&quot; alt=&quot;修改启动对象&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为 App.xaml 里面有实质上的有效代码（&lt;code class=&quot;highlighter-rouge&quot;&gt;StartupUri=&quot;MainWindow.xaml&quot;&lt;/code&gt;），所以我们需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;InitializeComponent&lt;/code&gt; 来使这段代码生效。打开 App.xaml.cs 文件，我们加一个构造函数：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public partial class App : Application
    {
&lt;span class=&quot;gi&quot;&gt;++      public App()
++      {
++          InitializeComponent();
++      }
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至此，我们便能直接启动我们的 Walterlv.InstallerUI 项目了。这对后续的调试很有用。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;实际上，这个 WPF UI 项目本应该做成 dll 而不是 exe，然后 App.xaml 和 App.xaml.cs 应该被删掉。这样，这个 WPF UI 项目就是一个纯 UI 库，用于后面被捆绑包调用。&lt;/p&gt;

  &lt;p&gt;但是，为了让这个项目与普通的 WPF 应用程序项目目录结构一样，也为了后续方便直接从 Visual Studio 启动调试，所以我刻意做成一个 exe，并保留了 App.xaml 和 App.xaml.cs 文件。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;将-wpf-ui-项目对接-wix-捆绑包&quot;&gt;将 WPF UI 项目对接 WiX 捆绑包&lt;/h2&gt;

&lt;h3 id=&quot;1-添加bootstrappercoredll引用&quot;&gt;1. 添加“BootstrapperCore.dll”引用&lt;/h3&gt;

&lt;p&gt;现在，为此 WPF UI 项目添加“BootstrapperCore.dll”引用：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在项目的“依赖项”上右键，“添加引用…”；&lt;/li&gt;
  &lt;li&gt;点击“浏览…”；&lt;/li&gt;
  &lt;li&gt;找到 Wix Toolset 的安装目录（如果没改，那么应该在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\WiX Toolset v3.11\SDK&lt;/code&gt; 这样的地方），找到 BootstrapperCore.dll 文件选中然后添加；&lt;/li&gt;
  &lt;li&gt;点击“确定”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-44-35.png&quot; alt=&quot;添加 BootstrapperCore.dll 引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意，WPF 项目并不像 WiX 项目一样针对引用的绝对路径进行了属性引用处理，因此我们需要自己来做这件事。双击 WPF UI 的项目名称以修改项目文件，将绝对路径改成环境变量引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(WIX)&lt;/code&gt;&lt;em&gt;（注意这里引用的是环境变量，而不是之前的 MSBuild 属性，虽然写法一模一样）&lt;/em&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;Reference Include=&quot;BootstrapperCore&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;HintPath&amp;gt;C:\Program Files (x86)\WiX Toolset v3.11\SDK\BootstrapperCore.dll&amp;lt;/HintPath&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;HintPath&amp;gt;$(WIX)\SDK\BootstrapperCore.dll&amp;lt;/HintPath&amp;gt;
&lt;/span&gt;      &amp;lt;/Reference&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;本教程前面为了不引入过多的复杂度，没有使用 &lt;a href=&quot;https://www.nuget.org/packages/WiX&quot;&gt;NuGet 包中的工具集&lt;/a&gt;而是单独下载了；所以实际上大家已经有了现成的工具集可供引用，我们才能使用此方法修改引用路径。并且此方法也能确保 WiX 打包项目使用的工具集版本和 WPF UI 项目使用的工具集版本一致。
然而使用 &lt;a href=&quot;https://www.nuget.org/packages/WiX&quot;&gt;WiX 的 NuGet 包&lt;/a&gt;有额外的好处，可以使得团队成员无需每人单独安装 Wix 工具集即可完整编译安装包。不过这涉及到一些 NuGet 相关的高级操作，我在其他博客里有说到。为了保证教程依然便于入门，所以我使用单独安装 WiX 工具集的方式来讲解。
如果大家感兴趣使用 NuGet 的方式来完成整个打包项目的构建，替代现在单独安装的方式，我可以单独再写一篇高级教程。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;2-实现自己的引导程序bootstrapper&quot;&gt;2. 实现自己的引导程序（Bootstrapper）&lt;/h3&gt;

&lt;p&gt;修改 Program.cs 文件，我们要实现自己的引导程序（Bootstrapper）：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  using Microsoft.Tools.WindowsInstallerXml.Bootstrapper;
&lt;/span&gt;
    namespace Walterlv.InstallerUI
    {
&lt;span class=&quot;gd&quot;&gt;--      public class Program
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      public class Program : BootstrapperApplication
&lt;/span&gt;        {
            private static int Main(string[] args)
            {
                // 这里的代码仅为调试使用，在最终的项目中无任何用途。
                var app = new App();
                return app.Run();
            }
&lt;span class=&quot;gi&quot;&gt;++
++          protected override void Run()
++          {
++              // 稍后要在这里添加安装流程控制。
++          }
&lt;/span&gt;        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在引导程序的入口代码（就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法）里，我们要实现基本的安装流程控制。最重要的，当然是向本文第一节那样，把 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; 跑起来。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  using System;
++  using System.Reflection;
++  using System.Windows;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
        protected override void Run()
        {
&lt;span class=&quot;gd&quot;&gt;--          // 稍后要在这里添加安装流程控制。
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          Engine.Log(LogLevel.Standard, &quot;Running the Walterlv.InstallerUI.&quot;);
++          try
++          {
++              LaunchUI();
++              Engine.Log(LogLevel.Standard, &quot;Exiting the Walterlv.InstallerUI.&quot;);
++              Engine.Quit(0);
++          }
++          catch (Exception ex)
++          {
++              Engine.Log(LogLevel.Error, $&quot;The Walterlv.InstallerUI is failed: {ex}&quot;);
++              Engine.Quit(-1);
++          }
++          finally
++          {
++              Engine.Log(LogLevel.Standard, &quot;The Walterlv.InstallerUI has exited.&quot;);
++          }
&lt;/span&gt;        }
&lt;span class=&quot;gi&quot;&gt;++
++      private int LaunchUI()
++      {
++          // 设置 WPF Application 的资源程序集，避免 WPF 自己找不到。
++          Application.ResourceAssembly = Assembly.GetExecutingAssembly();
++
++          // 正常启动 WPF Application。
++          var app = new App();
++          return app.Run();
++      }
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，我们做了这些事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法中全程记录日志（使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperApplication&lt;/code&gt; 基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;Engine&lt;/code&gt; 属性的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Log&lt;/code&gt; 方法来记）；&lt;/li&gt;
  &lt;li&gt;做了全局异常处理（避免因出现未知异常导致安装程序无法退出又看不到界面）；&lt;/li&gt;
  &lt;li&gt;正常退出安装过程（通过调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Engine.Quit&lt;/code&gt;）；&lt;/li&gt;
  &lt;li&gt;设置 WPF 资源程序集（默认情况下 WPF 会去入口程序集中找，但在 WiX 引导启动后入口程序集为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，所以我们需要手动指定资源程序集为本程序集）；&lt;/li&gt;
  &lt;li&gt;正常启动 WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;接下来，在 AssemblyInfo.cs 文件中，我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类标记为自定义的引导程序：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
++  using Microsoft.Tools.WindowsInstallerXml.Bootstrapper;

++  using Walterlv.InstallerUI;

++  [assembly: BootstrapperApplication(typeof(Program))]
&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至此，WPF UI 项目的纯 WPF 代码部分已经完成。&lt;/p&gt;

&lt;h3 id=&quot;3-添加-bootstrappercoreconfig-文件&quot;&gt;3. 添加 BootstrapperCore.config 文件&lt;/h3&gt;

&lt;p&gt;WiX 要引导到我们自己写的 WPF UI 上启动需要一个关键的配置文件 BootstrapperCore.config。在最终生成的捆绑包中，这个配置文件必须严格使用此名称。&lt;/p&gt;

&lt;p&gt;一个典型的 BootstrapperCore.config 文件的完整内容如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;sectionGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wix.bootstrapper&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;host&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/sectionGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;startup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useLegacyV2RuntimeActivationPolicy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedRuntime&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;sku=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework,Version=v4.6.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/startup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;wix.bootstrapper&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;host&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;assemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.InstallerUI&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedFramework&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4\Full&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/host&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/wix.bootstrapper&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个文件并不是 WPF UI 项目必须的文件，却是最终捆绑包所必须的文件。所以这个文件即可放到 WPF UI 项目中，也可放到 exe 的打包项目中。但是我们可以留意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;startup&amp;gt;&lt;/code&gt; 元素的全部内容跟一个标准 .NET Framework 应用程序的配置文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;startup&amp;gt;&lt;/code&gt; 元素一模一样，所以实际上我推荐把此文件放到 WPF UI 项目中，以提升代码复用性。&lt;/p&gt;

&lt;p&gt;于是，我们来创建这个文件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 WPF UI 项目上右键，“添加”，“新建项…”；&lt;/li&gt;
  &lt;li&gt;在模板中找到“应用程序配置文件”，然后点“添加”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-18-02-23.png&quot; alt=&quot;应用程序配置文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过模板生成的文件里有个空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;configuration&lt;/code&gt; 元素，我们可以无视；然后将以下内容复制到你的 App.config 文件中：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;sectionGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wix.bootstrapper&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;host&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/sectionGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;wix.bootstrapper&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;host&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;assemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.InstallerUI&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedFramework&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4\Full&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/host&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/wix.bootstrapper&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;请特别注意 1&lt;/strong&gt;：请加上述 &lt;code class=&quot;highlighter-rouge&quot;&gt;assemblyName&lt;/code&gt; 的值改成你的程序集名称（注意是程序集名称，不一定等于项目名）。&lt;/p&gt;

&lt;p&gt;请注意 2，相比于 BootstrapperCore.config 的典型内容，我们删除了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;startup&amp;gt;&lt;/code&gt; 元素，这是因为 .NET Framework 生成应用程序配置文件时，此元素会自动生成。&lt;/p&gt;

&lt;p&gt;还请注意 3，无论你使用 .NET Framework 4.x 的哪个版本，&lt;code class=&quot;highlighter-rouge&quot;&gt;wix.bootstrapper&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;host&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;supportedFramework&lt;/code&gt; 中的 &lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 值请始终设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;v4\Full&lt;/code&gt;&lt;/strong&gt;！不要设置成任何类似 &lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;v4.6.2&lt;/code&gt;&lt;/em&gt;、&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;v4.8&lt;/code&gt;&lt;/em&gt;、&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.NETFramework,Version=v4.5&lt;/code&gt;&lt;/em&gt; 之类的值。虽然官方文档有提到可以设置成 &lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;v3.5&lt;/code&gt;&lt;/em&gt;、&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;v4/Client&lt;/code&gt;&lt;/em&gt; 之类，但那会增加 WiX 打包项目配置的复杂程度，完成基本的打包需要配置更多（例如编写安装 .NET Framework 3.5 的代码）。&lt;/p&gt;

&lt;p&gt;至此，WPF UI 项目的所有代码已全部完成。&lt;/p&gt;

&lt;h2 id=&quot;在捆绑包项目里接入-wpf-ui-项目&quot;&gt;在捆绑包项目里接入 WPF UI 项目&lt;/h2&gt;

&lt;p&gt;现在回到我们的 Bundle.wxs 文件，我们需要做四件事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;设置捆绑包打包项目引用 WPF UI 项目；&lt;/li&gt;
  &lt;li&gt;将捆绑包引导程序设置成托管引导程序（&lt;code class=&quot;highlighter-rouge&quot;&gt;ManagedBootstrapperApplicationHost&lt;/code&gt;）；&lt;/li&gt;
  &lt;li&gt;将此 WPF UI 作为捆绑包负载放入捆绑包中；&lt;/li&gt;
  &lt;li&gt;将前面编写的 BootstrapperCore.config 文件作为捆绑包负载放入捆绑包中。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;还是像之前一样添加项目引用：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-18-54-39.png&quot; alt=&quot;添加 WPF UI 项目引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来我们一次性把对 Bundle.wxs 的所有改变代代码贴到下面：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;BootstrapperApplicationRef Id=&quot;WixStandardBootstrapperApplication.RtfLicense&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;BootstrapperApplicationRef Id=&quot;ManagedBootstrapperApplicationHost&quot;&amp;gt;
++    &amp;lt;Payload Name=&quot;BootstrapperCore.config&quot; SourceFile=&quot;$(var.Walterlv.InstallerUI.TargetDir)\$(var.Walterlv.InstallerUI.TargetFileName).config&quot;/&amp;gt;
++    &amp;lt;Payload SourceFile=&quot;$(var.Walterlv.InstallerUI.TargetPath)&quot; /&amp;gt;
++  &amp;lt;/BootstrapperApplicationRef&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解读：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们把 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperApplicationRef&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Id&lt;/code&gt; 换成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ManagedBootstrapperApplicationHost&lt;/code&gt;（含义为将引导程序引用的 Id 设置为托管引导程序宿主）。&lt;/li&gt;
  &lt;li&gt;我们把 WPF UI 项目中生成的配置文件用 &lt;code class=&quot;highlighter-rouge&quot;&gt;BootstrapperCore.config&lt;/code&gt; 这个名字放入到了负载中。（注意，WPF UI 项目中的 App.config 编译完成后生成的文件名为“程序集名.config”，在本教程中，为“Walterlv.InstallerUI.exe.config”，而我们通过引用项目变量的方式避免 WPF UI 项目的修改对这里的代码造成影响。）&lt;/li&gt;
  &lt;li&gt;我们把 WPF UI 项目中生成的文件加入到了负载中。（在本教程中，是“Walterlv.InstallerUI.exe”。另外，如果你的 WPF UI 项目有依赖的 dll，请一并在这里加入到负载，方法是依次写多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Payload&amp;gt;&lt;/code&gt; 元素，将依赖的 dll 设置到 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFile&lt;/code&gt; 属性中。）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至此，拥有 WPF UI 的最简单的 exe 安装包全部完成，你可以开始调试体验了。&lt;/p&gt;

&lt;h2 id=&quot;测试效果&quot;&gt;测试效果&lt;/h2&gt;

&lt;p&gt;现在编译 EXE 项目，然后双击运行看一下。&lt;/p&gt;

&lt;p&gt;如果发现无法运行，请前往此篇文章调试和解决问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-the-pit-you-might-step-on&quot;&gt;使用 WiX 创建最简单的安装包过程中可能出现的问题和解决方案汇总&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果可以正常运行，那么恭喜你，完成了 WiX 安装包入门教程的 Hello World 部分，可以进阶到入门教程的后续内容了。请回到目录：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;运行效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-19-36-48.png&quot; alt=&quot;自定义的 WPF 安装包界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关闭这个界面后，安装程序也将退出。&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;附上必要的源码，避免你在阅读教程时因模板文件的版本差异造成一些意料之外的问题。&lt;/p&gt;

&lt;p&gt;由于本文最终得到的源码较多，所以也同时放了一份到 GitHub 上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.WixInstallerDemo/tree/1b6600bb694c593894fc20cea76154b61ccf0c1f&quot;&gt;walterlv/Walterlv.WixInstallerDemo at 1b6600bb694c593894fc20cea76154b61ccf0c1f&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-19-42-23.png&quot; alt=&quot;必要的源码&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;bundlewxs&quot;&gt;Bundle.wxs&lt;/h3&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Bundle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainApp&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Manufacturer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;UpgradeCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;528f80ca-a8f5-4bd4-8131-59fdcd69a411&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BootstrapperApplicationRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ManagedBootstrapperApplicationHost&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Payload&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.InstallerUI.TargetPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Payload&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BootstrapperCore.config&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.InstallerUI.TargetDir)\$(var.Walterlv.InstallerUI.TargetFileName).config&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/BootstrapperApplicationRef&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Chain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageGroupRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NetFx462Web&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;MsiPackage&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Compressed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Chain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Bundle&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;appconfig&quot;&gt;App.config&lt;/h3&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;sectionGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wix.bootstrapper&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;host&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/sectionGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;wix.bootstrapper&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;host&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;assemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.InstallerUI&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedFramework&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4\Full&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/host&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/wix.bootstrapper&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;appxaml&quot;&gt;App.xaml&lt;/h3&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.InstallerUI.App&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.InstallerUI&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;StartupUri=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MainWindow.xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application.Resources&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application.Resources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;appxamlcs&quot;&gt;App.xaml.cs&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.InstallerUI&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Interaction logic for App.xaml&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;assemblyinfocs&quot;&gt;AssemblyInfo.cs&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Tools.WindowsInstallerXml.Bootstrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.InstallerUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BootstrapperApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThemeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ResourceDictionaryLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//where theme specific resource dictionaries are located&lt;/span&gt;
                                     &lt;span class=&quot;c1&quot;&gt;//(used if a resource is not found in the page,&lt;/span&gt;
                                     &lt;span class=&quot;c1&quot;&gt;// or application resource dictionaries)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ResourceDictionaryLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceAssembly&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//where the generic resource dictionary is located&lt;/span&gt;
                                              &lt;span class=&quot;c1&quot;&gt;//(used if a resource is not found in the page,&lt;/span&gt;
                                              &lt;span class=&quot;c1&quot;&gt;// app, or any theme specific resource dictionaries)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;programcs&quot;&gt;Program.cs&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Tools.WindowsInstallerXml.Bootstrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.InstallerUI&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BootstrapperApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这里的代码仅为调试使用，在最终的项目中无任何用途。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Running the Walterlv.InstallerUI.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;LaunchUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Exiting the Walterlv.InstallerUI.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;The Walterlv.InstallerUI is failed: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Walterlv.InstallerUI has exited.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LaunchUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 设置 WPF Application 的资源程序集，避免 WPF 自己找不到。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResourceAssembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 正常启动 WPF Application。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 11:52:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-wpf-installer-ui.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：为 WiX 制作的 msi 安装包添加 .NET Framework 环境检查</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;前面我们制作完成了一个简单的 msi 安装包。如果我们要安装的程序运行需要 .NET Framework 环境，那么也可以先进行 .NET Framework 版本检查。&lt;/p&gt;

&lt;p&gt;本文将指导你在 msi 安装前检查 .NET Framework 的版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文基于前一篇文章已经建好的项目继续：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;用 WiX 制作安装包：创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加-wixnetfxextension-引用&quot;&gt;添加 WixNetFxExtension 引用&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 msi 安装包项目的“Reference”上右键，“添加引用…”；&lt;/li&gt;
  &lt;li&gt;在打开的“Add Reference”窗口中确保选中的是“浏览”标签，然后在查找范围中找到 Wix Toolset 的安装目录（如果没改，那么应该在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\WiX Toolset v3.11\bin&lt;/code&gt; 这样的地方）；&lt;/li&gt;
  &lt;li&gt;在文件列表中找到“WixNetFxExtension.dll”；&lt;/li&gt;
  &lt;li&gt;点击“添加”；&lt;/li&gt;
  &lt;li&gt;点击“确定”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-10-08-48.png&quot; alt=&quot;添加 WixNetFxExtension.dll 引用&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;你不用担心绝对路径问题。&lt;/p&gt;

  &lt;p&gt;虽然我们前面选择的 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\WiX Toolset v3.11\bin&lt;/code&gt; 看起来是个绝对路径，但实际上在 wixproj 项目里记录的是一个属性引用，因此可以很容易在团队成员之间共享和跨版本迁移。&lt;/p&gt;

  &lt;p&gt;如下是 Walterlv.Installer.Msi.wixproj 项目文件中对 WixNetFxExtension 的引用代码：&lt;/p&gt;

  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WixExtension&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WixNetFxExtension&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;HintPath&amp;gt;&lt;/span&gt;$(WixExtDir)\WixNetFxExtension.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/HintPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;WixNetFxExtension&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixExtension&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;添加完 WixNetFxExtension 的引用后，还需要把它的命名空间添加到 Product.wxs 中。打开 Product.wxs 文件，在里面添加一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;Wix xmlns=&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;Wix xmlns=&quot;http://schemas.microsoft.com/wix/2006/wi&quot;
&lt;/span&gt;         xmlns:netfx=&quot;http://schemas.microsoft.com/wix/NetFxExtension&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，添加此命名空间不是必要操作，因为本教程后续没有用到此命名空间。&lt;/p&gt;

&lt;h2 id=&quot;编辑-productwxs&quot;&gt;编辑 Product.wxs&lt;/h2&gt;

&lt;p&gt;现在，我们需要编辑 Product.wxs 文件。做两件事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;得知当前系统环境是否已具备 .NET Framework 某版本；&lt;/li&gt;
  &lt;li&gt;根据判断结果决定此 MSI 包是否能被安装。&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;小提示&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;MSI 包只能判断 .NET Framework 是否存在，无法在不存在时执行 .NET Framework 的安装操作。如果需要安装 .NET Framework，你需要继续阅读本教程系列的 exe 打包部分。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;判断-net-framework-是否已满足要求&quot;&gt;判断 .NET Framework 是否已满足要求&lt;/h3&gt;

&lt;p&gt;因为我们已经引用了 WixNetFxExtension.dll，那里面已经写好了 .NET Framework 各版本是否存在的判断逻辑，所以我们只需要引用一下它的判断结果就好了。&lt;/p&gt;

&lt;p&gt;在 WiX 的配置文件 wxs 里，引用一个属性的方法是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyRef&amp;gt;&lt;/code&gt; 元素。所以，我们在 Product.wxs 里添加这样的一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Package InstallerVersion=&quot;200&quot; Compressed=&quot;yes&quot; InstallScope=&quot;perMachine&quot; /&amp;gt;

++  &amp;lt;PropertyRef Id=&quot;WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED&quot;/&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++
&lt;/span&gt;    &amp;lt;MajorUpgrade DowngradeErrorMessage=&quot;A newer version of [ProductName] is already installed.&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyRef&amp;gt;&lt;/code&gt; 元素必须是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Product&amp;gt;&lt;/code&gt; 元素的直接子级；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Package&amp;gt;&lt;/code&gt; 元素必须是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Product&amp;gt;&lt;/code&gt; 元素的第一个子级（也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyRef&amp;gt;&lt;/code&gt; 必须在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Package&amp;gt;&lt;/code&gt; 的后面）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;决定此-msi-包是否能被安装&quot;&gt;决定此 MSI 包是否能被安装&lt;/h3&gt;

&lt;p&gt;紧接在刚刚那句的后面，我们再添加一句：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;PropertyRef Id=&quot;WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED&quot;/&amp;gt;

++  &amp;lt;Condition Message=&quot;This application requires .NET Framework 4.6.2. Please install the .NET Framework then run this installer again.&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;![CDATA[WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED]]&amp;gt;
++  &amp;lt;/Condition&amp;gt;
++
&lt;/span&gt;    &amp;lt;MajorUpgrade DowngradeErrorMessage=&quot;A newer version of [ProductName] is already installed.&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这句话的意思是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;此 MSI 包安装需要满足指定条件&lt;/li&gt;
  &lt;li&gt;中间的判断条件我们用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;![CDATA[&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;]]&amp;gt;&lt;/code&gt; 包裹起来了，避免判断条件中出现了一些会破坏 XML 语法的字符（如 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; 等）出现导致意外的问题（但实际上在本例中，我们只用了字母和下划线，所以你也可以直接写 &lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED&lt;/code&gt;）；&lt;/li&gt;
  &lt;li&gt;如果不满足指定条件，则弹出提示信息，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 属性中指定不满足条件时要弹出的信息。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过，考虑到在卸载程序时无需检查 .NET Framework（反正也不会再运行了），所以我们可以在判断条件里加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;OR&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Condition Message=&quot;This application requires .NET Framework 4.6.2. Please install the .NET Framework then run this installer again.&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;![CDATA[WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED]]&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      Installed OR WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED
&lt;/span&gt;    &amp;lt;/Condition&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为我们的判断条件里没有使用到 XML 特殊字符，所以我刻意删掉了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;![CDATA[&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;]]&amp;gt;&lt;/code&gt; 以提升可读性。有的团队为避免出错要求强制加上此包裹，有的团队为了提升可读性建议如无必要则不要加上包裹。你也可以定义你的团队规范。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Installed&lt;/code&gt; 属性表示当前此产品是否已安装。也就是说新的判断条件的意思是：如果当前产品已安装，或者 .NET Framework 已安装有 4.6.2 或更高版本，则满足安装条件，准许安装，否则弹出错误提示。&lt;/p&gt;

&lt;h3 id=&quot;可供判断的-net-framework-版本&quot;&gt;可供判断的 .NET Framework 版本&lt;/h3&gt;

&lt;p&gt;WiX 已开源，其中 wix3 的代码在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/wixtoolset/wix3&quot;&gt;wixtoolset/wix3: WiX Toolset v3.x&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;/src/ext/NetFxExtension/wixlib&lt;/code&gt; 目录下有已定义好的各种 .NET Framework 版本的判断逻辑。我整理成下表，方便大家根据自己的需要查阅：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;.NET Framework 版本&lt;/th&gt;
      &lt;th&gt;对应判断属性&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4.8&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_48_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.2&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_472_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.1&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_471_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_47_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.2&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.1&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_461_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_46_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.2&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_452_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.1&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_451_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_45_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WIX_IS_NETFRAMEWORK_40_OR_LATER_INSTALLED&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;更低版本的 .NET Framework 没有直接的“是否安装”判断方法，需要根据版本号比较来判断，所以我不在此入门教程中列出。&lt;/p&gt;

&lt;p&gt;WiX 3 不支持 .NET Core 3.x、.NET 5 以及 .NET 6 的判断。如需检查这些环境，要么需要自己写判断方法（不属于此新手教程内容），要么需要升级到 WiX 4（本教程基于 WiX 3）。见：&lt;a href=&quot;https://github.com/wixtoolset/issues/issues/6108&quot;&gt;Support .NET Standard and/or .NET Core custom Bootstrapper · Issue #6108 · wixtoolset/issues&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;测试效果&quot;&gt;测试效果&lt;/h2&gt;

&lt;p&gt;现在，编译 MSI 项目，然后运行输出目录下的 msi 文件，你会……呃……看不到任何效果……因为我们的开发机上具备 .NET Framework 4.8 的环境，可完美运行 .NET Framework 4.6.2 需求的应用。&lt;/p&gt;

&lt;p&gt;下图是我魔改了 DEMO 后在 Windows 11 上的截图（放上来就是为了平衡美感的）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-10-45-38.png&quot; alt=&quot;假 .NET Framework 需求&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过为了真实性，我还是找了台 Windows 7 纯净系统来测试：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-10-55-07.png&quot; alt=&quot;.NET Framework 需求&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果点击“OK”，安装程序将直接退出，不会执行任何安装操作。&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;附上必要的源码，避免你在阅读教程时因模板文件的版本差异造成一些意料之外的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-11-50-39.png&quot; alt=&quot;必要的源码&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;productwxs&quot;&gt;Product.wxs&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;// 除了本文所说的改动外，本文件的其他内容均保持模板文件的原始模样。&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;xmlns:netfx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/NetFxExtension&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Product&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainApp&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1033&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Manufacturer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;UpgradeCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2aeffe1a-8bb6-4b06-b1c0-feca18e17cf7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InstallerVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Compressed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InstallScope=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;perMachine&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Message=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This application requires .NET Framework 4.6.2. Please install the .NET Framework then run this installer again.&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;![CDATA[WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED]]&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Condition&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MajorUpgrade&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DowngradeErrorMessage=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A newer version of [ProductName] is already installed.&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MediaTemplate&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Feature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductFeature&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Installer.Msi&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;ComponentGroupRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponents&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Feature&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Product&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TARGETDIR&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SourceDir&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProgramFilesFolder&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Installer.Msi&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ComponentGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponents&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Component&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;File&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.Demo.MainApp.TargetPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Component&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ComponentGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/check_for_dotnet.html&quot;&gt;How To: Check for .NET Framework Versions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.firegiant.com/wix/tutorial/com-expression-syntax-miscellanea/expression-syntax/&quot;&gt;Expression Syntax&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 11:46:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-msi-detect-net-framework.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-msi-detect-net-framework.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：创建一个简单的 msi 安装包</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;本文将带大家制作一个简单的 msi 安装包。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文操作基于系列教程中的一个最简项目，见 &lt;a href=&quot;/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution.md&quot;&gt;准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案&lt;/a&gt;。如果你没准备这样的项目，拿一个现成的项目也行，毕竟打包对目标程序的形式没有任何要求，只要是一个能跑起来的程序即可。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建-wix-msi-项目&quot;&gt;创建 WiX MSI 项目&lt;/h2&gt;

&lt;p&gt;在解决方案上右键，“添加”-&amp;gt;“新建项目…”，然后在“添加新项目”窗口中搜索“WiX”，找到“Setup Project for WiX v3”。按“下一步”取个名字，然后“创建”。&lt;/p&gt;

&lt;p&gt;注意，选择的模板要注意这些要点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;图标上标记了“wix”，标签上标记了“WiX”&lt;/li&gt;
  &lt;li&gt;模板简介中说明这是在创建“MSI”文件&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-28-01.png&quot; alt=&quot;创建 WiX MSI 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-34-44.png&quot; alt=&quot;取个名字&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;引用目标项目&quot;&gt;引用目标项目&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 WiX MSI 项目（在本教程中，我取的名字为 Walterlv.Installer.Msi）的“References”上右键，选“添加引用…”；&lt;/li&gt;
  &lt;li&gt;在打开的“Add Reference”窗口中选择“项目”标签；&lt;/li&gt;
  &lt;li&gt;选中希望打包的项目；&lt;/li&gt;
  &lt;li&gt;点“添加”；&lt;/li&gt;
  &lt;li&gt;点“确定”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-41-41.png&quot; alt=&quot;引用目标项目&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;编辑-productwxs-文件&quot;&gt;编辑 Product.wxs 文件&lt;/h2&gt;

&lt;h3 id=&quot;添加要打包的文件&quot;&gt;添加要打包的文件&lt;/h3&gt;

&lt;p&gt;在 Product.wxs 文件中，找到提示你放文件、注册表项和其他资源的注释“&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;!-- TODO: Insert files, registry keys, and other resources here. --&amp;gt;&lt;/code&gt;”：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;把周围的“Component”解除注释（因为我们真的要加打包的文件了）；&lt;/li&gt;
  &lt;li&gt;删除“TODO”注释（本教程会继续教你如何完成打包，不需要 TODO 提示了）；&lt;/li&gt;
  &lt;li&gt;在“Component”块中添加一行 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;File Source=&quot;$(var.Walterlv.Demo.MainApp.TargetPath)&quot; /&amp;gt;&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt;Fragment&amp;gt;
     &amp;lt;ComponentGroup Id=&quot;ProductComponents&quot; Directory=&quot;INSTALLFOLDER&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--     &amp;lt;!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. --&amp;gt;
--     &amp;lt;!-- &amp;lt;Component Id=&quot;ProductComponent&quot;&amp;gt; --&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++     &amp;lt;Component Id=&quot;ProductComponent&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--       &amp;lt;!-- TODO: Insert files, registry keys, and other resources here. --&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++       &amp;lt;File Source=&quot;$(var.Walterlv.Demo.MainApp.TargetPath)&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--     &amp;lt;!-- &amp;lt;/Component&amp;gt; --&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++     &amp;lt;/Component&amp;gt;
&lt;/span&gt;     &amp;lt;/ComponentGroup&amp;gt;
   &amp;lt;/Fragment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这里的 Walterlv.Demo.MainApp 是上一个步骤中引用的项目的名称（不是程序集或 exe 的名称）！如果你有自己的项目名，则在此改成你自己的项目名称。&lt;/li&gt;
  &lt;li&gt;本例的目标程序只有一个文件，因此我们只放了一行，如果你要打包多个文件，可返回本教程目录页查阅其他文章。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;编辑基本的安装包信息&quot;&gt;编辑基本的安装包信息&lt;/h3&gt;

&lt;p&gt;此时，我们距离完成 msi 打包只剩最后一步了，就是填写基本的安装包信息。因为如果你不填，编译会报错：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-16-05-32.png&quot; alt=&quot;缺少厂商信息&quot; /&gt;&lt;br /&gt;
▲ 缺少厂商信息&lt;/p&gt;

&lt;p&gt;这个信息在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Product&lt;/code&gt; 标签的特性上更改：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Product Id=&quot;*&quot;
&lt;span class=&quot;gd&quot;&gt;--           Name=&quot;Walterlv.Installer.Msi&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++           Name=&quot;Walterlv.Demo.MainApp&quot;
&lt;/span&gt;             Language=&quot;1033&quot;
             Version=&quot;1.0.0.0&quot;
&lt;span class=&quot;gd&quot;&gt;--           Manufacturer=&quot;&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++           Manufacturer=&quot;walterlv&quot;
&lt;/span&gt;             UpgradeCode=&quot;2aeffe1a-8bb6-4b06-b1c0-feca18e17cf7&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;除了改了厂商（&lt;code class=&quot;highlighter-rouge&quot;&gt;Manufacturer&lt;/code&gt;）之外，我还额外改了一下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt;，这个名字是最终出现在系统设置“应用和功能”中的名字，当然也是控制面板“卸载程序”中的名字。毕竟谁也不希望系统“应用和功能”里显示的名字不是真正的产品名吧……&lt;/p&gt;

&lt;p&gt;另外，其他属性的值也值得留意。但在你明白他们的真实含义之前，不建议修改其值。&lt;/p&gt;

&lt;p&gt;关于这些值的含义，你可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-installer-using-wix-best-practice-product-id-and-upgrade-code&quot;&gt;Windows 安装包制作最佳实践：ProductCode、UpgradeCode、PackageCode 应该怎么设置？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;修改输出文件名&quot;&gt;修改输出文件名&lt;/h3&gt;

&lt;p&gt;以上 Product.wxs 修改的是安装包的信息。如果希望更改 MSI 安装包的文件名，则需要去项目的属性页里去修改，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-16-21-35.png&quot; alt=&quot;修改 MSI 安装包文件名&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;测试效果&quot;&gt;测试效果&lt;/h2&gt;

&lt;p&gt;现在，我们完成了一个最简单的 MSI 安装包，测试安装一下。&lt;/p&gt;

&lt;p&gt;前往 MSI 文件的输出目录（在项目目录的 bin\Debug 下）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-16-24-31.png&quot; alt=&quot;前往 MSI 文件的输出目录&quot; /&gt;&lt;br /&gt;
▲ 前往 MSI 文件的输出目录&lt;/p&gt;

&lt;p&gt;安装完后，可以在系统设置“应用和功能”以及“Program Files”目录中找到它：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-16-24-10.png&quot; alt=&quot;系统设置应用和功能&quot; /&gt;&lt;br /&gt;
▲ 系统设置应用和功能&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-16-39-38.png&quot; alt=&quot;Program Files 文件夹&quot; /&gt;&lt;br /&gt;
▲ Program Files 文件夹&lt;/p&gt;

&lt;p&gt;测试完成后，记得及时卸载掉这个包。虽然这次没什么影响，但后续我们会学到的某个操作可能导致未及时卸载的包再也无法通过正常途径卸载，所以请保持良好的习惯。（虚拟机调试的小伙伴可无视）。&lt;/p&gt;

&lt;p&gt;另外，觉得不错可以提交一下代码，方便后续章节的学习。&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;附上必要的源码，避免你在阅读教程时因模板文件的版本差异造成一些意料之外的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-17-43-34.png&quot; alt=&quot;必要的源码&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;productwxs&quot;&gt;Product.wxs&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;// 除了本文所说的改动外，本文件的其他内容均保持模板文件的原始模样。&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Product&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainApp&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1033&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Manufacturer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;UpgradeCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2aeffe1a-8bb6-4b06-b1c0-feca18e17cf7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InstallerVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Compressed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InstallScope=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;perMachine&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MajorUpgrade&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DowngradeErrorMessage=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A newer version of [ProductName] is already installed.&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MediaTemplate&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Feature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductFeature&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Installer.Msi&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;ComponentGroupRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponents&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Feature&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Product&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TARGETDIR&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SourceDir&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProgramFilesFolder&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Installer.Msi&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ComponentGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponents&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Component&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;File&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.Demo.MainApp.TargetPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Component&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ComponentGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 11:46:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-msi-hello-world.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-msi-hello-world.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：为 WiX 制作的 exe 安装包添加 .NET Framework 前置的安装步骤</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;前面我们制作完成了一个简单的 exe 安装包。如果我们要安装的程序运行需要 .NET Framework 环境，那么可以检查 .NET Framework 是否安装，如果未安装则可将其装上。&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;小提示&lt;/p&gt;

  &lt;p&gt;Bundle（exe 格式）的判断方法和 Product（msi 格式）的不一样，因此 &lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-detect-net-framework&quot;&gt;为 WiX 制作的 msi 安装包添加 .NET Framework 环境检查&lt;/a&gt; 一文中所编写的代码对本文没有任何影响。因此即使跳过了那篇文章也丝毫不影响本文的学习。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;本文基于前一篇文章已经建好的项目继续：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-exe-hello-world&quot;&gt;用 WiX 制作安装包：创建一个简单的 exe 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;背景知识&quot;&gt;背景知识&lt;/h2&gt;

&lt;p&gt;在开始给我们的 exe 安装包增加 .NET Framework 环境检查之前，我们先了解一点背景知识，以便后续步骤可以使用更专业的词汇来准确描述我们正在做的事情。&lt;/p&gt;

&lt;p&gt;注意到我们解决方案里面的两个 wxs 文件了吗？他们分别是用来打 msi 包的 Product.wxs 和用来打 exe 包的 Bundle.wxs。这两个文件的结构分别像这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Product.wxs --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Product&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Package&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其他元素 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Product&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Bundle.wxs --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Bundle&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BootstrapperApplicationRef&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其他元素 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Product&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里引入了两个很重要的概念：产品（Product）和捆绑包（Bundle）。&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Product&amp;gt;&lt;/code&gt; 元素负责定义如何安装一个产品，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Bundle&amp;gt;&lt;/code&gt; 元素负责定义如何安装一组包。在 wxs 文件中，他们分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Wix&amp;gt;&lt;/code&gt; 元素的直接子级，彼此拥有不同的元素特性（Attribute）和子级（Child）——相互之间不可通用。也就是说，如果哪天你在网上某处扒出来某份 WiX 安装包代码，你需要清楚到底应该把这份代码放到哪个文件中。&lt;/p&gt;

&lt;p&gt;WiX 的官方文档中明确说明了这两个元素分别具有的不同特性和子级：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html&quot;&gt;Product Element&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html&quot;&gt;Bundle Element&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;添加-wixnetfxextension-引用&quot;&gt;添加 WixNetFxExtension 引用&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 exe 安装包项目的“Reference”上右键，“添加引用…”；&lt;/li&gt;
  &lt;li&gt;在打开的“Add Reference”窗口中确保选中的是“浏览”标签，然后在查找范围中找到 Wix Toolset 的安装目录（如果没改，那么应该在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\WiX Toolset v3.11\bin&lt;/code&gt; 这样的地方）；&lt;/li&gt;
  &lt;li&gt;在文件列表中找到“WixNetFxExtension.dll”；&lt;/li&gt;
  &lt;li&gt;点击“添加”；&lt;/li&gt;
  &lt;li&gt;点击“确定”。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-14-31-16.png&quot; alt=&quot;添加 WixNetFxExtension.dll 引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;与之前添加引用一样，虽然我们选的路径是绝对路径，但实际上写入到 wixproj 文件中的是一个属性引用，所以不会存在团队协作和跨版本迁移问题。&lt;/p&gt;

&lt;p&gt;上次我们添加 WixNetFxExtension.dll 的引用是为了引用一个属性。而这次，我们是为了引用一个 .NET Framework 的安装包。&lt;/p&gt;

&lt;h2 id=&quot;编辑-bundlewxs&quot;&gt;编辑 Bundle.wxs&lt;/h2&gt;

&lt;p&gt;现在，我们需要编辑 Bundle.wxs 文件。做两件事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将 .NET Framework 的安装加入到捆绑包的安装过程中；&lt;/li&gt;
  &lt;li&gt;将 .NET Framework 的安装包文件嵌入到捆绑包中或随包放到单独的文件中（可选）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;将-net-framework-的安装加入到捆绑包的安装过程中&quot;&gt;将 .NET Framework 的安装加入到捆绑包的安装过程中&lt;/h3&gt;

&lt;p&gt;WixNetFxExtension.dll 中已经自带好了各种版本的 .NET Framework 安装方法，其中每个版本都含在线安装和离线安装两种方法。&lt;/p&gt;

&lt;p&gt;对于小型项目，我们可以考虑在线安装 .NET Framework。于是，我们在 Bundle.wxs 文件中添加一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Chain&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;PackageGroupRef Id=&quot;NetFx462Web&quot;/&amp;gt;
&lt;/span&gt;      &amp;lt;MsiPackage Compressed=&quot;yes&quot;
                  SourceFile=&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;/&amp;gt;
    &amp;lt;/Chain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于面向大量用户的产品，我们可能需要考虑本地安装 .NET Framework。于是，我们在 Bundle.wxs 文件中添加另一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Chain&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;PackageGroupRef Id=&quot;NetFx462Redist&quot;/&amp;gt;
&lt;/span&gt;      &amp;lt;MsiPackage Compressed=&quot;yes&quot;
                  SourceFile=&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;/&amp;gt;
    &amp;lt;/Chain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上两种方式选择一种即可，看你的需要。&lt;/p&gt;

&lt;p&gt;WiX 已开源，其中 wix3 的代码在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/wixtoolset/wix3&quot;&gt;wixtoolset/wix3: WiX Toolset v3.x&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;/src/ext/NetFxExtension/wixlib&lt;/code&gt; 目录下有已定义好的各种 .NET Framework 版本的安装逻辑。我整理成下表，方便大家根据自己的需要查阅：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;.NET Framework 版本&lt;/th&gt;
      &lt;th&gt;在线安装&lt;/th&gt;
      &lt;th&gt;本地安装&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4.8&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx48Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx48Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.2&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx472Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.1&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.2&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx462Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx462Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.1&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx461Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx461Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx46Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx46Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.2&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx452Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx452Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.1&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx451Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx451Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx45Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx45Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4 Full&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx40Web&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx40Redist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4 Client Profile&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx40ClientWeb&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx40ClientRedist&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;其他版本未提供安装逻辑，如果需要，你得自己写。鉴于这部分需要用到更多代码，所以我不在本入门教程内说明。如果需要的话，我单独写一篇。&lt;/p&gt;

&lt;h3 id=&quot;将-net-framework-的安装包文件嵌入到捆绑包中&quot;&gt;将 .NET Framework 的安装包文件嵌入到捆绑包中&lt;/h3&gt;

&lt;p&gt;如果你在前一个步骤中选择的是通过 Web 的方式来安装 .NET Framework，那么此步骤就不需要了。而如果你打算将 .NET Framework 的安装包嵌入到捆绑包中或者随包放到单独的文件中，那么请继续操作。&lt;/p&gt;

&lt;p&gt;根据 &lt;a href=&quot;https://github.com/wixtoolset/wix3&quot;&gt;WiX 3 已开源的源码&lt;/a&gt;我们可以得知，本地安装 .NET Framework 时选取的目录为 &lt;code class=&quot;highlighter-rouge&quot;&gt;redist\&lt;/code&gt;。对于 Bundle 捆绑包来说，这个目录指代了两种意思：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;跟捆绑包的 exe 同一目录下的“redist”文件夹中；&lt;/li&gt;
  &lt;li&gt;捆绑包打包后包内的虚拟目录“redist”中。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这对应了两种本地安装时，.NET Framework 安装包的两种再分发（redistribute）方法。&lt;/p&gt;

&lt;p&gt;来看看怎么做：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;下载 .NET Framework 的离线安装包（&lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-framework&quot;&gt;官方下载地址（含各种版本）&lt;/a&gt;；&lt;/li&gt;
  &lt;li&gt;将下载好的 .NET Framework 安装包拖入到 exe 安装包项目中的根目录或任一文件夹下（也可以通过右键添加文件的方式添加）；&lt;/li&gt;
  &lt;li&gt;编辑 Bundle.wxs 文件，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;BootstrapperApplicationRef&amp;gt;&lt;/code&gt; 内加入负载；&lt;/li&gt;
  &lt;li&gt;编辑 Bundle.wxs 文件，把 &lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx462Web&lt;/code&gt; 改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;NetFx462Redist&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-15-12-27.png&quot; alt=&quot;下载 .NET Framework&quot; /&gt;&lt;br /&gt;
▲ 下载 .NET Framework&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;BootstrapperApplicationRef Id=&quot;WixStandardBootstrapperApplication.RtfLicense&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;BootstrapperApplicationRef Id=&quot;WixStandardBootstrapperApplication.RtfLicense&quot;&amp;gt;
++    &amp;lt;Payload Name=&quot;redist\NDP462-KB3151800-x86-x64-AllOS-ENU.exe&quot;
++             SourceFile=&quot;Assets\ndp462-kb3151800-x86-x64-allos-enu.exe&quot;/&amp;gt;
&lt;/span&gt;    &amp;lt;/BootstrapperApplicationRef&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--    &amp;lt;PackageGroupRef Id=&quot;NetFx462Web&quot;/&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;PackageGroupRef Id=&quot;NetFx462Redist&quot;/&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上四个步骤完成后，你的解决方案应该像下面这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-15-23-17.png&quot; alt=&quot;添加了 .NET Framework 负载的解决方案&quot; /&gt;&lt;/p&gt;

&lt;p&gt;解释一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我创建了一个“Assets”文件夹用于存放刚下载好的 .NET Framework 的离线安装包（为了避免读者在概念上产生混淆，我刻意避开使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;redist&lt;/code&gt; 这个名字，以示说明解决方案内的文件夹结构仅为开发文件夹结构，不代表最终捆绑包内的虚拟目录结构）。&lt;/li&gt;
  &lt;li&gt;我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;BootstrapperApplicationRef&amp;gt;&lt;/code&gt; 元素内新建了一个子元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Payload&amp;gt;&lt;/code&gt;（负载），其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt; 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;redist\NDP462-KB3151800-x86-x64-AllOS-ENU.exe&lt;/code&gt;（这个对应的就是最终捆绑包的虚拟目录结构），&lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFile&lt;/code&gt; 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\ndp462-kb3151800-x86-x64-allos-enu.exe&lt;/code&gt;（这个对应的是开发时项目中的文件结构）。&lt;/li&gt;
  &lt;li&gt;每个 .NET Framework 版本都有自己对应的文件名称，如果还想继续用 WixNetFxExtension.dll 中提供的安装 .NET Framework 的功能，那么从官网下载文件后就不能改名字（WiX 中定义这些文件名是全大写的，下载下来的是全小写的，虽然实际上大小写并不影响）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;编译这个项目，去输出目录下插件，可以发现几百 KB 的安装包现在变成了 59.6MB。很明显，.NET Framework 已经嵌入到了捆绑包中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-15-24-33.png&quot; alt=&quot;已嵌入了 .NET Framework 捆绑包的 exe 安装包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而如果你跳过前面加 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Payload&amp;gt;&lt;/code&gt; 的步骤，那么最终生成的的 exe 将不含 .NET Framework 的安装包。如果用户此时双击这个 exe 安装文件并且当前的 .NET Framework 版本不满足要求，则会弹出一个文件选择对话框，要求用户选择正确的 .NET Framework 安装文件以继续安装过程。如果你希望避免用户选择文件，那么就需要把安装包放到 exe 文件同级目录下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;redist&lt;/code&gt; 文件夹中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-15-45-07.png&quot; alt=&quot;不嵌入 .NET Framework 时的目录结构&quot; /&gt;&lt;/p&gt;

&lt;!-- 此方法在 WiX 3 中不生效 --&gt;
&lt;!-- ![不嵌入 .NET Framework 而是随包附带单独文件的方法](/static/posts/2021-07-15-15-37-43.png) --&gt;

&lt;h2 id=&quot;测试效果&quot;&gt;测试效果&lt;/h2&gt;

&lt;p&gt;现在，编译 MSI 项目，然后去没有 .NET Framework 4.6.2 的电脑上运行输出目录下的 exe 文件，可以看到已经在安装 .NET Framework 了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-15-44-25.png&quot; alt=&quot;正在安装 .NET Framework&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;附上必要的源码，避免你在阅读教程时因模板文件的版本差异造成一些意料之外的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-15-16-08-11.png&quot; alt=&quot;必要的源码&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;bundlewxs&quot;&gt;Bundle.wxs&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;// 除了本文所说的改动外，本文件的其他内容均保持模板文件的原始模样。&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Wix&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Bundle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainApp&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Manufacturer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;UpgradeCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;528f80ca-a8f5-4bd4-8131-59fdcd69a411&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BootstrapperApplicationRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WixStandardBootstrapperApplication.RtfLicense&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Payload&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;redist\NDP462-KB3151800-x86-x64-AllOS-ENU.exe&quot;&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\ndp462-kb3151800-x86-x64-allos-enu.exe&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/BootstrapperApplicationRef&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Chain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageGroupRef&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NetFx462Redist&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;MsiPackage&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Compressed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;SourceFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.Walterlv.Installer.Msi.TargetPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Chain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Bundle&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Wix&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 11:46:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-bundle-detect-and-install-net-framework.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-bundle-detect-and-install-net-framework.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：安装 WiX Toolset 系列 Visual Studio 插件</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;本文介绍安装 WiX Toolset 的两款 Visual Studio 插件，以便你能直接在 Visual Studio 里完整整套安装包的制作，无需使用命令行工具。对初学 WiX 的开发者来说比较友好。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于插件&quot;&gt;关于插件&lt;/h2&gt;

&lt;p&gt;Wix Toolset Visual Studio Extension 为 Visual Studio 带来了这些功能：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;通过模板创建 WiX 项目&lt;/li&gt;
  &lt;li&gt;支持 .wixproj 这个 WiX 专属的项目格式（只是新扩展名，里面的内容还是其他各种项目格式都用的那种）&lt;/li&gt;
  &lt;li&gt;为 WiX 打包项目提供专属的属性面板页，可供设置一些基本的属性&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;反正，装了这款插件能让你更容易编写和管理 WiX 安装包相关文件。&lt;/p&gt;

&lt;h2 id=&quot;安装插件&quot;&gt;安装插件&lt;/h2&gt;

&lt;p&gt;截至目前（2021年7月），WiX Toolset 的 Visual Studio 扩展最高支持到 VS2019，因此你需要在不高于 VS2019 的扩展管理里面下载插件。&lt;/p&gt;

&lt;p&gt;方法是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;启动 Visual Studio 2019，选“继续但无需代码”；&lt;/li&gt;
  &lt;li&gt;选菜单“扩展”-&amp;gt;“管理扩展”；&lt;/li&gt;
  &lt;li&gt;在“联机”页中搜索“WiX”，找到“Wix Toolset Visual Studio 2019 Extension”，然后点“下载”；&lt;/li&gt;
  &lt;li&gt;接下来，关闭所有已经打开的 Visual Studio，等待自动弹出的插件安装界面；&lt;/li&gt;
  &lt;li&gt;在 VSIX Installer 界面中，点击“Modify”以应用插件的安装。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-11-29-43.png&quot; alt=&quot;启动 Visual Studio 2019&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-14-43-45.png&quot; alt=&quot;打开“管理扩展”&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-14-47-25.png&quot; alt=&quot;在 VSIX Installer 中点击“Modify”&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其他说明&quot;&gt;其他说明&lt;/h2&gt;

&lt;p&gt;WiX 插件暂不支持 Visual Studio 2022，毕竟到了 Visual Studio 2022 开始 VS 使用 AMD64 架构了。&lt;/p&gt;

&lt;p&gt;如果你有自己的插件需要升级到支持 VS2022，可阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-vs2019-extension-support-to-vs2022.html&quot;&gt;Visual Studio 2022 出来啦！教你如何将 VS2019 的 VSIX 扩展/插件项目迁移到 VS2022&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 14 Jul 2021 09:47:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-installing-visual-studio-extensions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-installing-visual-studio-extensions.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：安装 WiX Toolset 工具集</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的首篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;WiX 提供一组工具集，我们的安装包正是通过这一组工具集来编译生成的。你可以通过很多方式来安装这组工具集，本文会提到多种方案，但仅会详细说其中一种，以便让教程尽可能简单。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;从官网下载安装-wix-toolset&quot;&gt;从官网下载安装 WiX Toolset&lt;/h2&gt;

&lt;p&gt;请前往其 GitHub 发布页下载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/wixtoolset/wix3/releases&quot;&gt;Releases · wixtoolset/wix3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了简单，可下载其中的 wix311.exe 文件。这份安装包可帮助我们更简单地部署好 WiX Toolset 的构建环境。&lt;/p&gt;

&lt;p&gt;点击中间最大的那个按钮“Install”即可开始安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-installing-wix-toolset.gif&quot; alt=&quot;安装 WiX Toolset&quot; /&gt;&lt;/p&gt;

&lt;p&gt;安装完成之后，在中间的按钮上它会提示可以安装 Visual Studio 集成：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-14-22-57.png&quot; alt=&quot;推荐安装 Visual Studio 集成&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击这个按钮后可安装 Visual Studio 插件。关于安装此插件的详细信息，可阅读下一篇入门博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-installing-visual-studio-extensions&quot;&gt;安装 WiX Toolset Visual Studio 插件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另特别说明一下，这个安装包界面是用 WPF 做的。是后续入门教程系列博客里也会说到如何做一个 WPF 界面的安装包。&lt;/p&gt;

&lt;h2 id=&quot;其他安装途径&quot;&gt;其他安装途径&lt;/h2&gt;

&lt;p&gt;此段非新手教程部分，如果不关心可略过。&lt;/p&gt;

&lt;h3 id=&quot;scoop-安装&quot;&gt;scoop 安装&lt;/h3&gt;

&lt;p&gt;如果你安装有 scoop 包管理器，可直接输入以下命令安装：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wixtoolset&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这套工具是在 scoop 的 main bucket 里面的，所以无需添加新的 bucket。安装完成后会自动添加环境变量，所以即便是 scoop 安装后，也可以直接在 Visual Studio 里面正常构建安装包。&lt;/p&gt;

&lt;h3 id=&quot;nuget-安装&quot;&gt;nuget 安装&lt;/h3&gt;

&lt;p&gt;以上方式均为全局安装，如果是团队合作，要求所有维护 WiX 安装包的开发人员都安装好 WiX Toolset。你也可以考虑将 WiX 安装到你的某个 Visual Studio 项目中，这样打开此项目的所有开发人员在还原 NuGet 包后都自动拥有了 WiX 全套工具集。&lt;/p&gt;

&lt;p&gt;使用 NuGet 的方式是按项目安装的，仅此解决方案（sln）有效。安装了此 NuGet 包的项目将可完全使用 WiX 工具集（因为包里包含了构建安装包需要的 MSBuild 属性）。&lt;/p&gt;

&lt;p&gt;以下是 NuGet 包中自带的属性一览：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixInstallPath&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tools&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixInstallPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixExtDir&amp;gt;&lt;/span&gt;$(WixInstallPath)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixExtDir&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixTargetsPath&amp;gt;&lt;/span&gt;$(WixInstallPath)\wix.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixTargetsPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LuxTargetsPath&amp;gt;&lt;/span&gt;$(WixInstallPath)\lux.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LuxTargetsPath&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixTasksPath&amp;gt;&lt;/span&gt;$(WixInstallPath)\WixTasks.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixTasksPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixSdkPath&amp;gt;&lt;/span&gt;$(WixInstallPath)\sdk\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixSdkPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WixCATargetsPath&amp;gt;&lt;/span&gt;$(WixSdkPath)wix.ca.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WixCATargetsPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果感兴趣通过 NuGet 的方式来安装 WiX Toolset，我可以再写一篇专门使用此方式安装并在团队所有人电脑上可直接构建安装包的博客。&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Jul 2021 09:47:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-installing-build-tools.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-installing-build-tools.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>用 WiX 制作安装包：准备一个用于学习 WiX 安装包制作的 Visual Studio 解决方案</title>
        <description>&lt;p&gt;本文是 &lt;a href=&quot;/post/getting-started-with-wix-toolset&quot;&gt;WiX Toolset 安装包制作入门教程&lt;/a&gt; 系列中的一篇，可前往阅读完整教程。&lt;/p&gt;

&lt;p&gt;严格来说，本文算不得教程，只是带大家创建一个需要被打包的项目。如果你本身对使用 Visual Studio 开发非常得心应手，本文完全可以跳过，你可以用你的任何一个现成的项目进行练手。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建示例项目&quot;&gt;创建示例项目&lt;/h2&gt;

&lt;p&gt;我这里拿一个控制台项目示例，当作被打包的对象。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-14-59-59.png&quot; alt=&quot;启动 Visual Studio 创建新项目&quot; /&gt;&lt;br /&gt;
▲ 启动 Visual Studio 创建新项目&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-08-59.png&quot; alt=&quot;选择控制台应用程序作为模板&quot; /&gt;&lt;br /&gt;
▲ 选择控制台应用程序作为模板&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-09-51.png&quot; alt=&quot;输入好项目和解决方案名称&quot; /&gt;&lt;br /&gt;
▲ 输入好项目和解决方案名称&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-10-53.png&quot; alt=&quot;选好目标框架&quot; /&gt;&lt;br /&gt;
▲ 选好目标框架&lt;/p&gt;

&lt;p&gt;那么，我们就创建好了一个最简单的项目：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-12-10.png&quot; alt=&quot;一个简单的项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们后续学习打包时，就需要打包这个项目生成的应用。&lt;/p&gt;

&lt;h2 id=&quot;加入-git-版本管理&quot;&gt;加入 git 版本管理&lt;/h2&gt;

&lt;p&gt;为了避免学习过程中各种修改导致文件无法还原，建议大家将此新项目加入到 git 版本管理中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-14-42.png&quot; alt=&quot;创建 Git 存储库&quot; /&gt;&lt;br /&gt;
▲ 创建 Git 存储库&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-15-15-18.png&quot; alt=&quot;仅限本地&quot; /&gt;&lt;br /&gt;
▲ 仅限本地&lt;/p&gt;

&lt;p&gt;现在，我们已经准备了一个最简单的项目，可以开始后续 WiX 打包的正式学习了。&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Jul 2021 09:47:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-wix-toolset-create-a-new-learning-vs-solution.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>WiX 安装包制作最佳实践：Id、UpgradeCode 应该怎么设置？</title>
        <description>&lt;p&gt;在 WiX 安装包制作时，Product.wxs 文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Product&lt;/code&gt; 标签中存在一些属性，这些属性应该如何设置才是比较合适的呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是对我另一篇入门教程博客的一点补充：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/getting-started-with-wix-toolset-msi-hello-world&quot;&gt;用 WiX 制作安装包：创建一个简单的 msi 安装包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;idversion&quot;&gt;Id、Version&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Id：产品 Id。&lt;/li&gt;
  &lt;li&gt;Version：产品版本。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于这两个值的变化：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果这两个值都没有更改而构建出一个新的 MSI 安装包，那么 Windows Installer 会认为这两个包之间属于“小型更新”（Update）。&lt;/li&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 属性更改，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Id&lt;/code&gt; 属性没有更改，那么 Windows Installer 会认为这两个包之间属于“次要升级”（Upgrade）。&lt;/li&gt;
  &lt;li&gt;如果这两个值都更改了，那么 Windows Installer 会认为这两个包之间属于“主要升级”（MajorUpgrade）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;特别把这几种升级类型的英文名称拿出来说，是因为我们在 Product.wxs 中配置升级策略时会使用到这些名称。了解这些升级方式有助于我们写出符合预期的升级策略。&lt;/p&gt;

&lt;p&gt;如果保持 Product.wxs 文件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Product&lt;/code&gt; 元素的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Id&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;，那么每次构建一个 MSI 文件都会视为一次“主要升级”（MajorUpgrade）。&lt;/p&gt;

&lt;p&gt;在没有配置升级策略的情况下，如果有两个不同的 MSI 包设置了相同的 Id 和 Version，那么当安装了其中一个之后，另一个将无法安装。双击 msi 文件时，Windows Installer 将弹出错误框：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-07-14-17-04-08.png&quot; alt=&quot;Id 相同的错误&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;upgradecode&quot;&gt;UpgradeCode&lt;/h2&gt;

&lt;p&gt;对于同一个产品，无论其产品 Id、Version 如何变化，都应该保持 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpgradeCode&lt;/code&gt; 不变，以便 Windows Installer 能准确认为这是同一个产品的“主要升级”（MajorUpgrade）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/win32/msi/patching-and-upgrades?redirectedfrom=MSDN&quot;&gt;修补和升级 - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.advancedinstaller.com/user-guide/product-identification.html&quot;&gt;Product Identification (ProductCode and UpgradeCode)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/26734471/6233938&quot;&gt;In WiX, where is the ProductCode specified? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/win32/msi/productcode?redirectedfrom=MSDN&quot;&gt;ProductCode 属性 - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 14 Jul 2021 09:46:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-installer-using-wix-best-practice-product-id-and-upgrade-code.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-installer-using-wix-best-practice-product-id-and-upgrade-code.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msi</category>
        
        <category>wix</category>
        
      </item>
    
      <item>
        <title>WPF 窗口和控件的 Unloaded 事件什么情况下不会触发</title>
        <description>&lt;p&gt;WPF 中如果监听窗口或者控件的的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件，那么这个事件会触发吗？答案是不确定的。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;示例代码&quot;&gt;示例代码&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.TempDemo.Wpf.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Unloaded=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window_Unloaded&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Closed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window_Closed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Unloaded=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid_Unloaded&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window_Unloaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 断点 1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Grid_Unloaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 断点 2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window_Closed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 断点 3&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你觉得以上事件中，断点都会进入吗？&lt;/p&gt;

&lt;h2 id=&quot;不确定的答案&quot;&gt;不确定的答案&lt;/h2&gt;

&lt;p&gt;在微软的&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.unloaded&quot;&gt;官方文档&lt;/a&gt;中说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note that the Unloaded event is not raised after an application begins shutting down. Application shutdown occurs when the condition defined by the ShutdownMode property occurs. If you place cleanup code within a handler for the Unloaded event, such as for a Window or a UserControl, it may not be called as expected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果应用程序正在关闭，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 时间将不会触发。WPF 通过设置在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 上的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShutdownMode&lt;/code&gt; 来决定是否在关闭窗口后关闭应用程序。因此，如果你试图通过在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件中执行清理操作，那么可能不会如预期般完成。&lt;/p&gt;

&lt;p&gt;因此，一般情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件是会触发的，但满足如下任一情况时，此事件将不不会触发：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.ShutdownMode=&quot;OnLastWindowClose&quot;&lt;/code&gt; 且最后一个窗口关闭时；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.ShutdownMode=&quot;OnMainWindowClose&quot;&lt;/code&gt; 且主窗口关闭时。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;顺序&quot;&gt;顺序&lt;/h2&gt;

&lt;p&gt;当触发 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件时，以上事件的触发顺序为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;断点 3&lt;/li&gt;
  &lt;li&gt;断点 1&lt;/li&gt;
  &lt;li&gt;断点 2&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/issues/1442&quot;&gt;Unloaded event not called on Window when app closed · Issue #1442 · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.unloaded&quot;&gt;FrameworkElement.Unloaded Event (System.Windows) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 30 Jun 2021 08:11:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-unloaded-event-not-fired.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-unloaded-event-not-fired.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>编译并体验 .NET MAUI 官方示例代码</title>
        <description>&lt;p&gt;在微软的 Build 2021 大会上，微软发布了 .NET 6 Preview 4，同时发布了于它的 MAUI 第四个预览版。在 MAUI 成为 Visual Studio 2022 的官方工作负载之前，成功编译并运行 MAUI 的示例程序会比较麻烦，本文旨在帮助大家完成示例程序的编译运行和体验。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;更新&lt;/strong&gt;：现在已经 .NET 6 Preview 5 了，配上 Visual Studio 2022 17.0 Preview 1 依然如本文这般麻烦。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;本段属太长不读系列。完整版请看下一段。&lt;/p&gt;

&lt;p&gt;截至 2021 年 5 月 31 日，要成功编译并运行 .NET MAUI 官方示例项目，你需要准备如下环境：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;安装 Visual Studio 2019 16.11 Preview 1 或更高版本（否则只能编译而无法运行，旧版 VS 不知道如何调试这种项目）&lt;/li&gt;
  &lt;li&gt;安装 .NET 6 Preview 4（MAUI 示例项目要求的最低 .NET 版本）&lt;/li&gt;
  &lt;li&gt;安装 maui-check，检查并修复所有环境问题（包含各类 SDK、模拟器等）&lt;/li&gt;
  &lt;li&gt;增加 NuGet 源 https://aka.ms/maui-preview/index.json（否则无法识别用到的 MAUI 类型）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在以上都准备就绪的情况下，你只需要使用 Visual Studio 2019 16.11 Preview 1 创建 MAUI 项目或打开官方 MAUI 示例项目即可调试 MAUI 项目。&lt;/p&gt;

&lt;p&gt;当然，预计 .NET 6 和 Visual Studio 2022 发布后，MAUI 将成为 Visual Studio 工作负载的一部分。届时，只需要在 Visual Studio 2022 里勾选 MAUI 就够了，其他什么也不用管。&lt;/p&gt;

&lt;h2 id=&quot;安装-visual-studio-2019-1611-preview-1-或更高版本&quot;&gt;安装 Visual Studio 2019 16.11 Preview 1 或更高版本&lt;/h2&gt;

&lt;p&gt;如果你电脑上已经安装过预览版的 Visual Studio，那么直接去开始菜单搜索并打开 Visual Studio Installer，然后把预览版更新到最新就好了。但如果你电脑上只有正式版的 Visual Studio，那么你需要前往预览版的下载地址下载一个预览版的在线安装包来安装。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/zh-hans/vs/preview/&quot;&gt;下载 Visual Studio 预览版（常年不变的地址）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-15-02-58.png&quot; alt=&quot;更新 Visual Studio 预览版&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装-net-6-preview-4&quot;&gt;安装 .NET 6 Preview 4&lt;/h2&gt;

&lt;p&gt;你还需要将你电脑上的 .NET 更新到 .NET 6 Preview 4 或者以上的版本。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet/6.0&quot;&gt;下载 .NET 6（常年不变的地址）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;安装-maui-check-工具&quot;&gt;安装 maui-check 工具&lt;/h2&gt;

&lt;p&gt;打开你喜爱的终端，然后输入如下命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;redth.net.maui.check&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这将在全局安装 maui-check 工具，辅助你完成 MAUI 开发环境的搭建。&lt;/p&gt;

&lt;p&gt;工具安装完成后，直接输入命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;maui-check&lt;/code&gt; 然后回车运行，工具将自动检查你的电脑上是否已完成 MAUI 开发环境的搭建。它会在检查到问题之后发出轻轻的一声“嘟”，然后问你：“要尝试修复吗？（! Attempt to fix?）”你只需要打 &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; 告诉它要修复就好了。&lt;/p&gt;

&lt;p&gt;这样的问题会问很多次，你都需要答 &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; 修复，甚至可能还需要多次运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;maui-check&lt;/code&gt; 工具来进行修复。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-15-12-11.png&quot; alt=&quot;Attempt to fix? [y/n] (y)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;到最后，当你再次运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;maui-check&lt;/code&gt; 时，它会兴奋地告诉你：“Congratulations, everything looks great!”这意味着，MAUI 所需的环境已全部搭建完成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-15-19-09.png&quot; alt=&quot;MAUI 所需环境已全部搭建完成&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果 maui-check 不断失败，可阅读本文末尾一节。&lt;/p&gt;

&lt;h2 id=&quot;增加-nuget-源&quot;&gt;增加 NuGet 源&lt;/h2&gt;

&lt;p&gt;我有另一篇博客介绍如何添加 NuGet 源，详细的方法你可以去那里看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.walterlv.com/post/add-custom-nuget-source.html&quot;&gt;全局或为单独的项目添加自定义的 NuGet 源 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要简单一点，你只需要在命令行中输入：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maui-preview&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://aka.ms/maui-preview/index.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这会直接修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;%AppData%\NuGet\NuGet.Config&lt;/code&gt; 文件，并在其中添加一行 NuGet 源。&lt;/p&gt;

&lt;h2 id=&quot;maui-官方示例仓库&quot;&gt;MAUI 官方示例仓库&lt;/h2&gt;

&lt;p&gt;在以上所有步骤执行完成之后，以下项目就能直接在 Visual Studio 2019 16.11 Preview 1 或更高版本中编译并调试了。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/maui-samples&quot;&gt;dotnet/maui-samples: .NET 6 preview samples. Not for production use. The main branch tracks the current preview release, and develop tracks the upcoming preview.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/davidortinau/WeatherTwentyOne&quot;&gt;davidortinau/WeatherTwentyOne&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;例如，以 &lt;a href=&quot;https://github.com/dotnet/maui-samples&quot;&gt;maui-samples&lt;/a&gt; 项目举例，将 HelloMaui 项目设为启动项目，在 Visual Studio 中将启动框架设置为 .net6.0-android，就可以在 Android 模拟器中运行 HelloMaui 应用了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-15-34-18.png&quot; alt=&quot;设置启动框架&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以下是 HelloMaui 在 Android 模拟器中的运行效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-hello-maui-sample.gif&quot; alt=&quot;官方 DEMO 在 Android 模拟器中的运行效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;另外，Visual Studio 2019 16.11 Preview 1 中已经内置了 MAUI 的项目模板，你也可以直接新建 MAUI 项目自行调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-15-37-54.png&quot; alt=&quot;新建 MAUI 项目&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其他问题&quot;&gt;其他问题&lt;/h2&gt;

&lt;h3 id=&quot;无法创建-android-模拟器&quot;&gt;无法创建 Android 模拟器&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Android&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Emulator&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x86&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;API30&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Google&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'s not created.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果 maui-check 时出现 Android 模拟器无法创建的错误（就像下图这样），可尝试在 Visual Studio 里手工创建一个 Android 模拟器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-30-36.png&quot; alt=&quot;Android Emulator - x86 - API30 - Google API's not created.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Visual Studio 里手工创建 Android 模拟器的方法如下：&lt;/p&gt;

&lt;p&gt;第一步：打开 Android 设备管理器&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-33-04.png&quot; alt=&quot;Android 设备管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二步：创建新设备&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-33-58.png&quot; alt=&quot;创建新设备&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建时，要注意操作系统必须选择“R 11.0 - API 30”，这是 MAUI 示例应用要求的最低版本。其他随意，然后点“创建 ”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-35-02.png&quot; alt=&quot;R 11.0 - API 30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建完后，等待下载、解压直至安装完成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-37-03.png&quot; alt=&quot;安装模拟器完成&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第三步：重新使用 maui-check 检查&lt;/p&gt;

&lt;p&gt;这时，应该就能全部通过检查了。&lt;/p&gt;

&lt;h3 id=&quot;无法调试-winui3-项目&quot;&gt;无法调试 WinUI3 项目&lt;/h3&gt;

&lt;p&gt;调试官方示例中的 HelloMauiWinUI3 项目时，你可能会遇到 COM 异常“没有注册类”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-31-16-27-21.png&quot; alt=&quot;没有注册类&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原因是，你应该将“HelloMauiWinUI3 (Package)”后缀的 WinUI 项目设为启动项，而不应该将“HelloMauiWinUI3”设为启动项。并且，调试启动时，应该选 Local Machine。&lt;/p&gt;

&lt;p&gt;以下是我运行另一个“Weather TwentyOne”官方示例应用的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-17-20-06.png&quot; alt=&quot;Weather Twenty One&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-net-maui-preview-4/&quot;&gt;Announcing .NET MAUI Preview 4 - .NET Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/maui/issues/1127&quot;&gt;[Bug] An unhandled exception of type ‘System.Reflection.TargetInvocationException’ occurred in System.Private.CoreLib.dll · Issue #1127 · dotnet/maui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 25 Jun 2021 09:36:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-maui-official-samples.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-maui-official-samples.html</guid>
        
        
        <category>dotnet</category>
        
        <category>maui</category>
        
      </item>
    
      <item>
        <title>Visual Studio 2022 出来啦！教你如何将 VS2019 的 VSIX 扩展/插件项目迁移到 VS2022</title>
        <description>&lt;p&gt;从 Visual Studio 2022 开始，Visual Studio 正式启用了 amd64 架构。为了确保扩展的兼容性，Visual Studio 2022 不会启用以前编译过的扩展，即使以前编译过的扩展把支持的 Visual Studio 版本号加到了 17.0（对应 VS2022）也不行。毕竟 x64 的进程真加载一个 x86 的程序集时，会炸得体无完肤。&lt;/p&gt;

&lt;p&gt;因为保证安全，Visual Studio 2022 仅加载专门为它开发和编译过的插件。&lt;/p&gt;

&lt;p&gt;如果你正好有一个为 Visual Studio 2019（或更早）开发的插件，那么可以通过阅读本文完成对插件项目的升级，以支持 Visual Studio 2022。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;tldr-最简改法&quot;&gt;TL;DR 最简改法&lt;/h2&gt;

&lt;p&gt;如果你赶时间，只想马上把项目改好，那么阅读这一小节就够了。&lt;/p&gt;

&lt;p&gt;首先我们确认一下，你原来的项目至少是这样的结构：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;是一个 Visual Studio 扩展项目&lt;/li&gt;
  &lt;li&gt;有一个 Visual Studio 扩展清单文件 source.extension.vsixmanifest&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-13-39-38.png&quot; alt=&quot;项目结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在此基础上，你需要修改两个文件。&lt;/p&gt;

&lt;p&gt;source.extension.vsixmanifest：&lt;/p&gt;

&lt;p&gt;请将原来的安装目标改成 17.0 以前和以后两个，以前的用 x86 架构，以后的用 amd64 架构。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Installation&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--    &amp;lt;InstallationTarget Id=&quot;Microsoft.VisualStudio.Community&quot; Version=&quot;[16.0,)&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;InstallationTarget Id=&quot;Microsoft.VisualStudio.Community&quot; Version=&quot;[16.0, 17.0)&quot;&amp;gt;
++      &amp;lt;ProductArchitecture&amp;gt;x86&amp;lt;/ProductArchitecture&amp;gt;
++    &amp;lt;/InstallationTarget&amp;gt;
++    &amp;lt;InstallationTarget Id=&quot;Microsoft.VisualStudio.Community&quot; Version=&quot;[17.0, 18.0)&quot;&amp;gt;
++      &amp;lt;ProductArchitecture&amp;gt;amd64&amp;lt;/ProductArchitecture&amp;gt;
++    &amp;lt;/InstallationTarget&amp;gt;
&lt;/span&gt;    &amp;lt;/Installation&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;*.csproj 文件：&lt;/p&gt;

&lt;p&gt;必须将 VS 构建工具升级到 17.0 或以上版本。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;PackageReference Include=&quot;Microsoft.VSSDK.BuildTools&quot; Version=&quot;16.9.1050&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;PackageReference Include=&quot;Microsoft.VSSDK.BuildTools&quot; Version=&quot;17.0.2140-preview2&quot; /&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你还有引用其他的 VS 构建工具，请一并升级到 17.0 或以上版本。升级时，此 VSIX 项目引用的其他项目（例如基于 .NET Standard 的分析器项目）无需升级 NuGet 包。&lt;/p&gt;

&lt;p&gt;至此，你再编译这个 Visual Studio 扩展项目，即可正常在旧的 Visual Studio 2019 和新的 Visual Studio 2022 上安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-13-46-19.png&quot; alt=&quot;支持两个 VS 版本的 VSIX&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;完整改法&quot;&gt;完整改法&lt;/h2&gt;

&lt;p&gt;如果你比较强迫症，我还是建议你完整改完整个项目。完整改完后，你将获得如下好处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 Visual Studio 2022 里双击 source.extension.vsixmanifest 后能打开专属的清单编辑器，避免手写容易出现明显错误&lt;/li&gt;
  &lt;li&gt;csproj 项目文件里不会有之前版本为了解决一些特定的 bug 而额外写的 bugfix 代码&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;完整改法，即使用 Visual Studio 2022 来创建新的 VSIX 扩展项目。&lt;/p&gt;

&lt;h3 id=&quot;第一步请确保已安装-sdk&quot;&gt;第一步：请确保已安装 SDK&lt;/h3&gt;

&lt;p&gt;在开始菜单找到并启动 Visual Studio Installer，然后确保勾选 Visual Studio 扩展开发的工作负载，并将右边的 .NET Compiler Platform SDK 勾选。前者提供编写和调试扩展的能力，而后者提供了新建模板和 Roslyn 相关工具。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-13-55-18.png&quot; alt=&quot;安装工作负载&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第二步重新创建-vsix-项目&quot;&gt;第二步：重新创建 VSIX 项目&lt;/h3&gt;

&lt;p&gt;新建一个 VSIX 项目：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-13-58-41.png&quot; alt=&quot;重建 VSIX 项目&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第三步复制并替换整个扩展文件&quot;&gt;第三步：复制并替换整个扩展文件&lt;/h3&gt;

&lt;p&gt;你可以把新创建项目的 csproj 文件和 source.extension.vsixmanifest 文件替换掉原项目的这两个文件，然后保留原项目。也可以考虑反过来操作，将原项目里的代码（如果有的话）放到新项目里来，然后保留新项目。&lt;/p&gt;

&lt;p&gt;合并这两个项目时，记得 source.extension.vsixmanifest 文件里的清单信息要与原来的保持一致，这样才能对原来的扩展进行升级（而不会创建出新的扩展来）。&lt;/p&gt;

&lt;p&gt;如果需要一个修改示例，你可以看我的一个 Pull Request（拉取请求）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/pull/28&quot;&gt;为插件添加 Visual Studio 2022 的支持 by walterlv · Pull Request #28 · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;重新上传扩展到市场&quot;&gt;重新上传扩展到市场&lt;/h2&gt;

&lt;p&gt;前往 Visual Studio 扩展市场的管理界面 &lt;a href=&quot;https://marketplace.visualstudio.com/manage&quot;&gt;https://marketplace.visualstudio.com/manage&lt;/a&gt;，需要登录。&lt;/p&gt;

&lt;p&gt;在你需要升级的扩展旁边的“…”里点“Edit”编辑。重新上传你新编译出来的 VSIX 文件，等待审核即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-25-14-04-59.png&quot; alt=&quot;重新上传扩展&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MicrosoftDocs/visualstudio-docs/blob/177db460a2dbd7de2876e2ad564795294dd1c80a/docs/extensibility/migration/update-visual-studio-extension.md&quot;&gt;visualstudio-docs/update-visual-studio-extension.md at master · MicrosoftDocs/visualstudio-docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 25 Jun 2021 06:05:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-vs2019-extension-support-to-vs2022.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-vs2019-extension-support-to-vs2022.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>两个方法，让 WPF 绘制的笔迹更加平滑</title>
        <description>&lt;p&gt;在 WPF 中绘制笔迹的时候，你可能会注意到绘制的笔迹非常的……呃……棱角分明。这在鼠标绘制的时候大家基本都能接受，但如果遇到一些触摸框报告触摸点也那么稀疏，那么写的字很不好看。另外，还有可能绘制的笔迹点来源于其他设备，通过网络传输而来，这时更容易遇到稀疏的点。&lt;/p&gt;

&lt;p&gt;本文将用两种方法来让 WPF 的笔迹更加平滑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;两种方法&quot;&gt;两种方法&lt;/h2&gt;

&lt;p&gt;我们有两种方法来解决这样的问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;点插值&lt;/li&gt;
  &lt;li&gt;曲线拟合&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;点插值&quot;&gt;点插值&lt;/h2&gt;

&lt;p&gt;如果导致不平滑的主要原因是点太稀疏，那么采用点插值算法可以解决很大的问题。常用的点插值算法是贝赛尔插值算法。&lt;/p&gt;

&lt;p&gt;通过贝赛尔插值算法的具体算法和代码，可参考这篇博客：&lt;a href=&quot;https://blog.csdn.net/Iron_Ye/article/details/82949401&quot;&gt;一种简单的贝塞尔拟合算法_Iron 的博客-CSDN博客&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;使用以上插值算法后的效果如下（两次分别绘制，因此笔迹不一样）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-21-17-36-55.png&quot; alt=&quot;插值前&quot; /&gt;&lt;br /&gt;
▲ 插值前&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-21-17-37-24.png&quot; alt=&quot;插值后&quot; /&gt;&lt;br /&gt;
▲ 插值后&lt;/p&gt;

&lt;h2 id=&quot;曲线拟合&quot;&gt;曲线拟合&lt;/h2&gt;

&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stroke&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingAttributes.FitToCurve&lt;/code&gt; 属性可开启或关闭笔迹的曲线拟合。&lt;/p&gt;

&lt;p&gt;这个属性是实时生效的，所以你可以在绘制笔迹的任何时刻设置它。例如一开始绘制时设置，你将可以在书写的过程中实时得到平滑的曲线，但用户可以明显看到绘制笔迹的过程中曲线拟合的过程（可看到笔迹在来回摆动）；你也可以在笔迹绘制结束插入到画布时再设置，这样在插入时用户只会看到一次笔迹的突变。&lt;/p&gt;

&lt;p&gt;使用以上曲线拟合后的效果如下（两次分别绘制，因此笔迹不一样）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-21-17-36-55.png&quot; alt=&quot;拟合前&quot; /&gt;&lt;br /&gt;
▲ 拟合前&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-21-17-37-53.png&quot; alt=&quot;拟合后&quot; /&gt;&lt;br /&gt;
▲ 拟合后&lt;/p&gt;

&lt;h2 id=&quot;综合使用&quot;&gt;综合使用&lt;/h2&gt;

&lt;p&gt;正常情况下，仅“点插值”就足够让笔记看起来很平滑了。但如果你要求笔迹看不出棱角（例如此笔迹非用户直接书写，而是从其他平台传输过来呈现），那么你可能需要采用“曲线拟合”。然而，如果你觉得无法忍受“曲线拟合”带来的笔迹来回摆动，那么可考虑将两个方法结合起来使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/Iron_Ye/article/details/82949401&quot;&gt;一种简单的贝塞尔拟合算法_Iron 的博客-CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 21 Jun 2021 09:38:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-smooth-ink.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-smooth-ink.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>C# 的事件，一般你不需要担心它的线程安全问题！</title>
        <description>&lt;p&gt;时不时会有小伙伴跟我提到在 C# 写事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 以及 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 时可能遇到线程安全问题。然而实际上这些操作并不会有线程安全问题，所以我特别写一篇博客来说明一下，从原理层面说说为什么不会有线程安全问题。&lt;/p&gt;

&lt;p&gt;顺便再提一下哪种情况下你却可能遇到线程安全问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;委托是不可变类型&quot;&gt;委托是不可变类型&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;委托是不可变类型。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这点很重要，这是 C# 事件一般使用场景不会发生线程安全问题的关键！&lt;/p&gt;

&lt;p&gt;那既然委托是不可变类型，那我们在写 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 以及引发事件的时候，是如何处理最新注册或注销的事件呢？&lt;/p&gt;

&lt;h2 id=&quot;-和---的本质&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的本质&lt;/h2&gt;

&lt;p&gt;我们随便写一个类型，里面包含一个事件：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.TempDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoClass&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从外表上，这个事件就像一个字段一样的不线程安全。但实际上，他像一个属性一样能处理好线程安全问题。&lt;/p&gt;

&lt;p&gt;众所周知，这个事件会编译成以下两个方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;add_SomeEvent&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;remove_SomeEvent&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Methods&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;specialname&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_SomeEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerGeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Header Size: 12 bytes&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Code Size: 41 (0x29) bytes&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// LocalVarSig Token: 0x11000001 RID: 1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxstack&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/* 0x0000025C 02           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x0000025D 7B01000004   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldfld&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000262 0A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// loop start (head: IL_0007)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000263 06           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000264 0B           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000265 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000266 03           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000267 280D00000A   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000026C 740D000001   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;castclass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000271 0C           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000272 02           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0016&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000273 7C01000004   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0017&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldflda&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000278 08           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000279 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000027A 280100002B   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000027F 0A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0023&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000280 06           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000281 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0025&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x00000282 33DF         */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0026&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;un&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// end loop&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000284 2A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0028&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method DemoClass::add_SomeEvent&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000002 RID: 2 RVA: 0x00002088 File Offset: 0x00000288&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;specialname&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;remove_SomeEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerGeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Header Size: 12 bytes&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Code Size: 41 (0x29) bytes&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// LocalVarSig Token: 0x11000001 RID: 1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxstack&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/* 0x00000294 02           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000295 7B01000004   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldfld&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x0000029A 0A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// loop start (head: IL_0007)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000029B 06           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000029C 0B           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000029D 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000029E 03           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x0000029F 280F00000A   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002A4 740D000001   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;castclass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002A9 0C           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002AA 02           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0016&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002AB 7C01000004   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0017&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldflda&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B0 08           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B1 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B2 280100002B   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B7 0A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0023&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B8 06           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002B9 07           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0025&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 0x000002BA 33DF         */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0026&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;un&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// end loop&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x000002BC 2A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0028&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method DemoClass::remove_SomeEvent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 本质上是调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Combine&lt;/code&gt; 方法和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Remove&lt;/code&gt; 方法。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Combine&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Remove&lt;/code&gt; 不会修改原委托，只会生成新的委托。&lt;/p&gt;

&lt;p&gt;于是，任何时候当你拿到这个事件的一个实例，并将它存在一个变量里之后，只要不给这个变量额外赋值，这个变量包含的已注册的委托数就已经完全确定了下来。之后无论什么时候再 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 这个事件，已经跟这个变量无关了。&lt;/p&gt;

&lt;h2 id=&quot;delegatecombine-和-delegateremove&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Combine&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Remove&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;现在让我们再来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Combine&lt;/code&gt; 的实现（&lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 就不举例了，相反操作）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotNullIfNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotNullIfNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CombineImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终调用了实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CombineImpl&lt;/code&gt; 方法，不过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate&lt;/code&gt; 基类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CombineImpl&lt;/code&gt; 方法没有实现（只有个异常）。&lt;/p&gt;

&lt;p&gt;为了实现事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt;，事件实际上是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MultiCastDelegate&lt;/code&gt; 类型，其实现如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This method will combine this delegate with the passed delegate&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    to form a new delegate.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombineImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;follow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;follow&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Verify that the types are the same...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InternalEqualTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;follow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Arg_DlgtTypeMis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;MulticastDelegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MulticastDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;follow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_invocationList&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_invocationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_invocationList&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NewMulticastDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_invocationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TrySetSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TrySetSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allocCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allocCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;allocCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allocCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dFollow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;followList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NewMulticastDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;计算好新委托所需的委托列表和个数后，创建一个新的委托实例，然后用计算所得的结果初始化它。这座实了委托不变，于是不存在线程安全问题。&lt;/p&gt;

&lt;h2 id=&quot;线程安全的事件引发&quot;&gt;线程安全的事件引发&lt;/h2&gt;

&lt;p&gt;从 C# 6.0 开始，大家引发事件都喜欢使用下面这样的方式：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不用担心，这就是线程安全的写法！&lt;/p&gt;

&lt;p&gt;以上这个写法是空传递写法，相当于：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们前面已经通过原理证实了“委托不变”，所以这里我们用变量存这个事件的时候，这个变量就完全确认了此时此刻已经注册的所有委托，后面的判空和引发都不会受与之发生在同一时刻的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的影响。&lt;/p&gt;

&lt;p&gt;有人说以上写法有可能会被编译器优化掉（《CLR via C#》说的），造成意料之外的线程安全问题，于是推荐写成下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Volatile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样写当然是没有问题的。可是这样就没有 C#6.0 带来的一句话写下来的畅快感了！实际上，你根本无需担心编译器会对你引发事件带来线程不安全的优化，因为现在的 C# 编译器和 .NET 运行时很聪明，非常清楚你是在引发事件，于是不会随便优化掉你这里的逻辑。&lt;/p&gt;

&lt;p&gt;归根结底，只需要用 C# 6.0 的空传递操作符写引发事件就没有问题了。&lt;/p&gt;

&lt;h2 id=&quot;是否可能出现线程不安全的情况呢&quot;&gt;是否可能出现线程不安全的情况呢？&lt;/h2&gt;

&lt;p&gt;从前面原理层面的剖析，我们可以明确知道，普通的事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 和引发是不会产生线程安全问题的；但这不代表任何情况你都不会遇到线程安全问题。&lt;/p&gt;

&lt;p&gt;如果你引发事件的代码逻辑比较复杂，涉及到多次读取事件成员（例如前面例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SomeEvent&lt;/code&gt;），那么依然会出现线程安全问题，因为你无法保证两次读取事件成员时，期间没有发生过事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;关于----的额外说明&quot;&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的额外说明&lt;/h2&gt;

&lt;p&gt;在上文写完之后，有小伙伴说，C# 里面 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 不是线程安全的，并举了以下例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当并发调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddValue&lt;/code&gt; 时，可能导致部分调用的结果被另一部分覆盖，从而出现线程安全问题。&lt;/p&gt;

&lt;p&gt;因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value += i&lt;/code&gt; 这个语法糖相当于以下句子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，事件没有这样的问题，因为事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 语法糖相当于以下句子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// demo.SomeEvent += DemoClass_SomeEvent;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 相当于：&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoClass_SomeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意这是一次函数调用，并没有像普通的数值运算一样执行两步计算；所以至少这一次方法调用不会有问题。&lt;/p&gt;

&lt;p&gt;那么，&lt;code class=&quot;highlighter-rouge&quot;&gt;add_SomeEvent&lt;/code&gt; 里面是线程安全的吗？如果只是单纯 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.Combine&lt;/code&gt; 然后赋值当然不是线程安全，但它不是简单赋值，而是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Interlocked.CompareExchange&lt;/code&gt; 原子操作赋值，在保证线程安全的同时还确保了性能：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* 0x000002B2 280100002B   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_001E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;转换成容易理解的 C# 代码大约是这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompareExchange&lt;/code&gt; 的返回值与第三个参数相同，说明本次原子操作成功完成，那么赋值有效，退出循环。&lt;/li&gt;
  &lt;li&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompareExchange&lt;/code&gt; 的返回值与第三个参数不同，说明本次原子操作冲突，在下一次循环中重试赋值。&lt;/li&gt;
  &lt;li&gt;因为赋值是很迅速的，所以即使大量并发，也只会有少数冲突，整体是非常快的。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;完整的 IL 代码可以在本文前面看到。这里的 !!0 是引用第 0 号泛型类型，即找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompareExchange(!!T$, !!T, !!T):!!T&lt;/code&gt; 重载。&lt;/p&gt;
</description>
        <pubDate>Fri, 18 Jun 2021 07:42:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/thread-safety-of-csharp-event.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/thread-safety-of-csharp-event.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Windows 中的 HRESULT</title>
        <description>&lt;p&gt;Windows 协议文档中所描述的协议规范中，错误码使用 HRESULT、Win32 错误码和 NTSTATUS 来描述。本文科普一下 HRESULT。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个简单的例子&quot;&gt;一个简单的例子&lt;/h2&gt;

&lt;p&gt;我们先举一个大家可能常用的 HRESULT 例子，这样后面的介绍能更简单一点。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x80070070&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将它改写成二进制：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0111&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0111&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 |
| - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | --&gt;

&lt;p&gt;它的意思是“There is not enough space on the disk.”即“磁盘空间不足。”&lt;/p&gt;

&lt;h2 id=&quot;规范中的-hresult&quot;&gt;规范中的 HRESULT&lt;/h2&gt;

&lt;p&gt;按照规范，HRESULT 的格式如下，其中首行的数字代表第几位（bit）：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;th&gt;5~15&lt;/th&gt;
      &lt;th&gt;16~31&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;S&lt;/td&gt;
      &lt;td&gt;R&lt;/td&gt;
      &lt;td&gt;C&lt;/td&gt;
      &lt;td&gt;N&lt;/td&gt;
      &lt;td&gt;X&lt;/td&gt;
      &lt;td&gt;Facility&lt;/td&gt;
      &lt;td&gt;Code&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;S&lt;/strong&gt;: 1 位，表示严重性。为 1 表示此结果为失败，为 0 表示此结果为成功。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;R&lt;/strong&gt;: 1 位，保留位。如果 N 位是 0，那么此位必须也是 0。如果 N 位是 1，那么此位由 NTSTATUS 定义的数字范围决定。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;: 1 位，自定义位。为 1 表示由微软定义，为 0 表示由其他厂商定义。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;N&lt;/strong&gt;: 1 位。为 1 表示此结果为 NTSTATUS 错误码。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;X&lt;/strong&gt;: 1 位。保留位，应设为 0。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Facility&lt;/strong&gt;: 11 位。设施代码。指定错误来源。后面的列表中有已定义的错误源，微软偶尔会添加新的种类。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Code&lt;/strong&gt;: 16 位（2 字节）。错误码的其他部分，指定错误的具体细节。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，Facility 设施代码的详细列表可以参见这里：&lt;a href=&quot;https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a&quot;&gt;[MS-ERREF]: HRESULT - Microsoft Docs&lt;/a&gt;。对 Win32 开发来说，0x7 是很常见的，表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;FACILITY_WIN32&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;win32-错误码&quot;&gt;Win32 错误码&lt;/h2&gt;

&lt;p&gt;现在再来看我们前面的例子：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0111&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0111&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;严重性：1，表示失败&lt;/li&gt;
  &lt;li&gt;设施代码：0x7，表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;FACILITY_WIN32&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;错误码：0x70，表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_DISK_FULL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所有的 Win32 错误码应该仅使用 16 位来表示，即范围从 0x0000 到 0xFFFF。关于 Win32 错误码的详细列表可以参见这里：&lt;a href=&quot;https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d&quot;&gt;[MS-ERREF]: Win32 Error Codes - Microsoft Docs&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;微软错误查询工具&quot;&gt;微软错误查询工具&lt;/h2&gt;

&lt;p&gt;如果你遇到了某个 Win32 错误码，或者 HRESULT 值，那么可以使用微软错误查询工具（The Microsoft Error Lookup Tool）查询其含义。&lt;/p&gt;

&lt;p&gt;下载地址：&lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=100432&quot;&gt;Download Microsoft Error Lookup Tool from Official Microsoft Download Center&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-16-10-36-42.png&quot; alt=&quot;错误查询工具&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;在-netc-代码中的使用&quot;&gt;在 .NET/C# 代码中的使用&lt;/h2&gt;

&lt;p&gt;例如，我们可能需要在一些 IO 操作中处理好磁盘空间已满的情况：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SaveFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDiskFullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 磁盘空间已满。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于磁盘空间已满没有对应的 .NET Exception，所以我们只能通过提取 &lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HResult&lt;/code&gt; 属性来判断操作的 HRESULT 值。&lt;/p&gt;

&lt;p&gt;我们定义了一个扩展方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsDiskFullException&lt;/code&gt;，实现如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// There is not enough space on the disk.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 磁盘空间不足。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ERROR_DISK_FULL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0070&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 判断某个 &amp;lt;see cref=&quot;IOException&quot;/&amp;gt; 是否是“磁盘空间不足”的异常。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;ex&quot;&amp;gt;IO 异常。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsDiskFullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ERROR_DISK_FULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a&quot;&gt;MS-ERREF: HRESULT - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/HRESULT&quot;&gt;HRESULT - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/debug/system-error-code-lookup-tool&quot;&gt;The Microsoft Error Lookup Tool - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 16 Jun 2021 02:51:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hresult-in-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hresult-in-windows.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>无法加载为扩展“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior”注册的类型</title>
        <description>&lt;p&gt;一天，某用户反馈过来说我们的软件无法运行，我一看异常信息看到了这个：“&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Configuration.ConfigurationErrorsException: 无法加载为扩展“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior”注册的类型“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior, Microsoft.VisualStudio.Diagnostics.ServiceModelSink, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”。 (C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config line 232)&lt;/code&gt;”。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;异常&quot;&gt;异常&lt;/h2&gt;

&lt;p&gt;异常的完整堆栈如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigurationErrorsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;无法加载为扩展“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Behavior&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”注册的类型“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Behavior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neutral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PublicKeyToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b03f5f7f11d50a3a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.30319&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;232&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EvaluateOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SectionInput&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isTrusted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FactoryRecord&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factoryRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SectionRecord&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FactoryRecord&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factoryRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SectionRecord&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getLkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSectionRecursive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getLkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestIsHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSectionRecursive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getLkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestIsHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSectionRecursive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getLkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestIsHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSectionRecursive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getLkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestIsHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRuntimeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseConfigurationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientConfigurationSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IInternalConfigSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigurationManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AspNetEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnsafeGetSectionFromConfigurationManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AspNetEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnsafeGetConfigurationSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigurationHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnsafeGetAssociatedSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContextInformation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evalContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sectionPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LookupCommonBehaviors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContextInformation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadServiceDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHostBase&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addBaseAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;skipHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHostBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadConfigurationSectionInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHostBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ApplyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ApplyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHostBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UriSchemeKeyedCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriSchemeKeyedCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoLib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WCF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Duplex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientInfoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientInfoBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPCLinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPCCloudLinkProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientInfoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientInfoBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPCLinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPCLinkProviderFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IIPCLinkEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientInfoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientInfoBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPCLinkInitializeStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStartupContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c__DisplayClass0_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JoinAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b__0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MoveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;---&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;引发异常的上一位置中堆栈跟踪的末尾&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;---&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowForNonSuccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HandleNonSuccessAndDebuggerNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JoinAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d__0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MoveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;---&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;引发异常的上一位置中堆栈跟踪的末尾&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;---&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowForNonSuccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HandleNonSuccessAndDebuggerNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupTaskWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c__DisplayClass36_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecuteTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b__1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MoveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;初步探索&quot;&gt;初步探索&lt;/h2&gt;

&lt;p&gt;这个异常消息提示基本已经把表层原因说得很明白了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigurationErrorsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;无法加载为扩展“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Behavior&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”注册的类型“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Behavior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceModelSink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neutral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PublicKeyToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b03f5f7f11d50a3a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.30319&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;232&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;即“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config”文件的 232 行有一个关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior&lt;/code&gt; 注册的类型无法加载。我打开那个文件，看到了相关行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;commonBehaviors&amp;gt;&amp;lt;endpointBehaviors&amp;gt;&amp;lt;Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior/&amp;gt;&amp;lt;/endpointBehaviors&amp;gt;&amp;lt;serviceBehaviors&amp;gt;&amp;lt;Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior/&amp;gt;&amp;lt;/serviceBehaviors&amp;gt;&amp;lt;/commonBehaviors&amp;gt;&amp;lt;/system.serviceModel&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;修复方法&quot;&gt;修复方法&lt;/h2&gt;

&lt;p&gt;将这一行里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior&lt;/code&gt; 部分删除后问题即解决。&lt;/p&gt;

&lt;p&gt;也就是说，这一行会变成：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;commonBehaviors&amp;gt;&amp;lt;endpointBehaviors&amp;gt;&amp;lt;/endpointBehaviors&amp;gt;&amp;lt;serviceBehaviors&amp;gt;&amp;lt;/serviceBehaviors&amp;gt;&amp;lt;/commonBehaviors&amp;gt;&amp;lt;/system.serviceModel&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于元素开闭不匹配的问题不用关心，放到整个文件中是匹配的。（不知道是什么程序写成这样的格式化乱的 XML 文件。）&lt;/p&gt;

&lt;p&gt;记得要以管理员权限保存。如果目标电脑没有好用的编辑器，可将其复制到桌面等低权限的目录下，编辑好再放回去。&lt;/p&gt;

&lt;h2 id=&quot;额外说明&quot;&gt;额外说明&lt;/h2&gt;

&lt;p&gt;无需担心删除这一行会造成什么不良影响，因为正常情况下没有装 Visual Studio 的电脑上，这个文件本就不应该有这一行的。（感谢 @kkwpsv 在 Win7/10 虚拟机中的试验。）&lt;/p&gt;

&lt;p&gt;至于目标电脑上究竟是为什么会导致没有 Visual Studio 时注册了一个 WCF 的行为扩展，这就不得而知了……（如果你知道，欢迎评论区教教我！）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[ServiceModel Registration Tool (ServiceModelReg.exe) - WCF&lt;/td&gt;
          &lt;td&gt;Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/framework/wcf/servicemodelreg-exe?redirectedfrom=MSDN)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/07ed7bd1-2c73-430b-a414-9a57e3aaf371/what-is-microsoftvisualstudiodiagnosticsservicemodelsinkdll?forum=wcf&quot;&gt;What is Microsoft.VisualStudio.Diagnostics.ServiceModelSink.dll?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/40757383/6233938&quot;&gt;.net - microsoft.visualstudio.diagnostics.servicemodelsink.dll – present on most systems, missing on new system - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/26732621/microsoft-visualstudio-diagnostics-servicemodelsink-behavior-could-not-be-load&quot;&gt;visual studio 2010 - ‘Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior’ could not be loaded - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17001861/wcf-and-windows-store-apps-configurationerrorsexception&quot;&gt;c# - WCF and Windows Store apps, ConfigurationErrorsException - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/6271112/wcf-routing-service-and-unable-to-automatically-debug-service-name-the-remo&quot;&gt;iis 7 - WCF: Routing service and “Unable to automatically debug ‘service name’. The remote procedure could not be debugged - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 11 Jun 2021 06:34:28 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wcf-exception-microsoft-visualstudio-diagnostics-servicemodelsink-behavior.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wcf-exception-microsoft-visualstudio-diagnostics-servicemodelsink-behavior.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET 单个异步任务如何同时监听多个取消请求（CancellationToken）</title>
        <description>&lt;p&gt;异步编程中，并不是所有时候 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等的都是新的异步任务；有时候同一个异步任务可能被多次等待，并且每个等待都可以有自己的取消请求，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;。那么如何在一个异步任务中同时响应多个取消请求呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;可被多次-await-的单个任务&quot;&gt;可被多次 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的单个任务&lt;/h2&gt;

&lt;p&gt;我们先来列举一个最简单的例子，用来作为多次取消请求的示例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemoClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_currentCancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 省略真正的异步代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，&lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomethingAsync&lt;/code&gt; 可能被调用多次，但执行的都是同一件事情。当任务完成时所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 全部等待完成，当任务取消时所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 全部取消。&lt;/p&gt;

&lt;h2 id=&quot;合并-cancellationtoken&quot;&gt;合并 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;合并 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; 的方法是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationTokenSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateLinkedTokenSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;合并完成后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; 在两者任一个取消时都会被取消。&lt;/p&gt;

&lt;p&gt;于是我们前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomethingAsync&lt;/code&gt; 加一行即可：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    class WalterlvDemoClass
    {
        private readonly CancellationToken _currentCancellationToken = default;

        public async Task DoSomethingAsync(CancellationToken cancellationToken)
        {
&lt;span class=&quot;gi&quot;&gt;++          _currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(_currentCancellationToken, cancellationToken);
++
&lt;/span&gt;            // 省略真正的异步代码，需要判断取消请求时，判断 _currentCancellationToken。
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/how-to-listen-for-multiple-cancellation-requests&quot;&gt;How to: Listen for Multiple Cancellation Requests - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 11 Jun 2021 01:13:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/a-single-task-listen-to-multiple-cancellation-requests.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/a-single-task-listen-to-multiple-cancellation-requests.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目）</title>
        <description>&lt;p&gt;默认情况下，我们打包 NuGet 包时，目标项目安装我们的 NuGet 包会引用我们生成的库文件（dll）。除此之外，我们也可以专门做 NuGet 工具包，还可以做 NuGet 源代码包。然而做源代码包可能是其中最困难的一种了，目标项目安装完后，这些源码将直接随目标项目一起编译。&lt;/p&gt;

&lt;p&gt;本文将从零开始，教你制作一个支持 .NET 各种类型项目的源代码包。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;前置知识&quot;&gt;前置知识&lt;/h2&gt;

&lt;p&gt;在开始制作一个源代码包之间，建议你提前了解项目文件的一些基本概念：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然就算不了解也没有关系。跟着本教程你也可以制作出来一个源代码包，只不过可能遇到了问题的时候不容易调试和解决。&lt;/p&gt;

&lt;h2 id=&quot;制作一个源代码包&quot;&gt;制作一个源代码包&lt;/h2&gt;

&lt;p&gt;接下来，我们将从零开始制作一个源代码包。&lt;/p&gt;

&lt;p&gt;我们接下来的将创建一个完整的解决方案，这个解决方案包括：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一个将打包成源代码包的项目&lt;/li&gt;
  &lt;li&gt;一个调试专用的项目（可选）&lt;/li&gt;
  &lt;li&gt;一个测试源代码包的项目（可选）&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;第一步创建一个-net-项目&quot;&gt;第一步：创建一个 .NET 项目&lt;/h3&gt;

&lt;p&gt;像其他 NuGet 包的引用项目一样，我们需要创建一个空的项目。不过差别是我们需要创建的是控制台程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-17-50-20.png&quot; alt=&quot;创建项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当创建好之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数中的所有内容都是不需要的，于是我们删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数中的所有内容但保留 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数。&lt;/p&gt;

&lt;p&gt;这时 Program.cs 中的内容如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.PackageDemo.SourceCode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;双击创建好的项目的项目，或者右键项目 “编辑项目文件”，我们可以编辑此项目的 csproj 文件。&lt;/p&gt;

&lt;p&gt;在这里，我将目标框架改成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;net48&lt;/code&gt;。实际上如果我们不制作动态源代码生成，那么这里无论填写什么目标框架都不重要。在这篇博客中，我们主要篇幅都会是做静态源代码生成，所以你大可不必关心这里填什么。&lt;/p&gt;

&lt;p&gt;提示：&lt;em&gt;如果 net48 让你无法编译这个项目，说明你电脑上没有装 .NET Framework 4.8 框架，请改成 net473, net472, net471, net47, net462, net 461, net46, net45, netcoreapp3.0, netcoreapp2.1, netcoreapp2.0 中的任何一个可以让你编译通过的目标框架即可。&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第二步组织项目的目录结构&quot;&gt;第二步：组织项目的目录结构&lt;/h3&gt;

&lt;p&gt;接下来，我们会让这个项目像一个 NuGet 包的样子。当然，是 NuGet 源代码包。&lt;/p&gt;

&lt;p&gt;请在你的项目当中创建这些文件和文件夹：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Assets
    - build
        + Package.props
        + Package.targets
    - buildMultiTargeting
        + Package.props
        + Package.targets
    - src
        + Foo.cs
    - tools
+ Program.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; 号表示文件夹，&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; 号表示文件。&lt;/p&gt;

&lt;p&gt;Program.cs 是我们一开始就已经有的，可以不用管。src 文件夹里的 Foo.cs 是我随意创建的一个类，你就想往常创建正常的类文件一样创建一些类就好了。&lt;/p&gt;

&lt;p&gt;比如我的 Foo.cs 里面的内容很简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.PackageDemo.SourceCode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv is a 逗比.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;props 和 targets 文件你可能在 Visual Studio 的新建文件的模板中找不到这样的模板文件。这不重要，你随便创建一个文本文件，然后将名称修改成上面列举的那样即可。接下来我们会依次修改这些文件中的所有内容，所以无需担心模板自动为我们生成了哪些内容。&lt;/p&gt;

&lt;p&gt;为了更直观，我将我的解决方案截图贴出来，里面包含所有这些文件和文件夹的解释。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-18-22-20.png&quot; alt=&quot;目录结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我特别说明了哪些文件和文件夹是必须存在的，哪些文件和文件夹的名称一定必须与本文说明的一样。如果你是以教程的方式阅读本文，建议所有的文件和文件夹都跟我保持一样的结构和名称；如果你已经对 NuGet 包的结构有一定了解，那么可自作主张修改一些名称。&lt;/p&gt;

&lt;h3 id=&quot;第三步编写项目文件-csproj&quot;&gt;第三步：编写项目文件 csproj&lt;/h3&gt;

&lt;p&gt;现在，我们要双击项目名称或者右键“编辑项目文件”来编辑项目的 csproj 文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-18-41-20.png&quot; alt=&quot;编辑项目文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们编辑项目文件的目的，是让我们前一步创建的项目文件夹结构真正成为 NuGet 包中的文件夹结构。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 要求此项目编译时要生成一个 NuGet 包。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里为了方便，我将 NuGet 包的输出路径设置在了解决方案根目录的 bin 文件夹下，而不是项目的 bin 文件夹下。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageOutputPath&amp;gt;&lt;/span&gt;..\bin\$(Configuration)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 创建 NuGet 包时，项目的输出文件对应到 NuGet 包的 tools 文件夹，这可以避免目标项目引用我们的 NuGet 包的输出文件。
         同时，如果将来我们准备动态生成源代码，而不只是引入静态源代码，还可以有机会运行我们 Program 中的 Main 函数。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/span&gt;tools&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildOutputTargetFolder&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此包将不会传递依赖。意味着如果目标项目安装了此 NuGet 包，那么安装目标项目包的项目不会间接安装此 NuGet 包。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 包的版本号，我们设成了一个预览版；当然你也可以设置为正式版，即没有后面的 -alpha 后缀。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;0.1.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 设置包的作者。在上传到 nuget.org 之后，如果作者名与 nuget.org 上的账号名相同，其他人浏览包是可以直接点击链接看作者页面。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 设置包的组织名称。我当然写成我所在的组织 dotnet 职业技术学院啦。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Company&amp;gt;&lt;/span&gt;dotnet-campus&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Company&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在生成 NuGet 包之前，我们需要将我们项目中的文件夹结构一一映射到 NuGet 包中。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IncludeAllDependencies&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_GetPackageFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 将 Package.props / Package.targets 文件的名称在 NuGet 包中改为需要的真正名称。
           因为 NuGet 包要自动导入 props 和 targets 文件，要求文件的名称必须是 包名.props 和 包名.targets；
           然而为了避免我们改包名的时候还要同步改四个文件的名称，所以就在项目文件中动态生成。--&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\Package.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).props&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\Package.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\Package.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\$(PackageId).props&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\Package.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 我们将 src 目录中的所有源代码映射到 NuGet 包中的 src 目录中。--&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\src\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第四步编写编译文件-targets&quot;&gt;第四步：编写编译文件 targets&lt;/h3&gt;

&lt;p&gt;接下来，我们将编写编译文件 props 和 targets。注意，我们需要写的是四个文件的内容，不要弄错了。&lt;/p&gt;

&lt;p&gt;如果我们做好的 NuGet 源码包被其他项目使用，那么这四个文件中的其中一对会在目标项目被自动导入（Import）。在你理解 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt; 一文内容之前，你可能不明白“导入”是什么意思。但作为从零开始的入门博客，你也不需要真的理解导入是什么意思，只要知道这四个文件中的代码将在目标项目编译期间运行就好。&lt;/p&gt;

&lt;h4 id=&quot;buildmultitargeting-文件夹中的-packageprops-文件&quot;&gt;buildMultiTargeting 文件夹中的 Package.props 文件&lt;/h4&gt;

&lt;p&gt;你只需要将下面的代码拷贝到 buildMultiTargeting 文件夹中的 Package.props 文件即可。注意将包名换成你自己的包名，也就是项目名。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 为了简单起见，如果导入了这个文件，那么我们将直接再导入 ..\build\Walterlv.PackageDemo.SourceCode.props 文件。
       注意到了吗？我们并没有写 Package.props，因为我们在第三步编写项目文件时已经将这个文件转换为真实的包名了。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\build\Walterlv.PackageDemo.SourceCode.props&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;buildmultitargeting-文件夹中的-packagetargets-文件&quot;&gt;buildMultiTargeting 文件夹中的 Package.targets 文件&lt;/h4&gt;

&lt;p&gt;你只需要将下面的代码拷贝到 buildMultiTargeting 文件夹中的 Package.targets 文件即可。注意将包名换成你自己的包名，也就是项目名。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 为了简单起见，如果导入了这个文件，那么我们将直接再导入 ..\build\Walterlv.PackageDemo.SourceCode.targets 文件。
       注意到了吗？我们并没有写 Package.targets，因为我们在第三步编写项目文件时已经将这个文件转换为真实的包名了。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\build\Walterlv.PackageDemo.SourceCode.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;build-文件夹中的-packageprops-文件&quot;&gt;build 文件夹中的 Package.props 文件&lt;/h4&gt;

&lt;p&gt;下面是 build 文件夹中 Package.props 文件的全部内容。可以注意到我们几乎没有任何实质性的代码在里面。即便我们在此文件中还没有写任何代码，依然需要创建这个文件，因为后面第五步我们将添加更复杂的代码时将再次用到这个文件完成里面的内容。&lt;/p&gt;

&lt;p&gt;现在，保持你的文件中的内容与下面一模一样就好。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;build-文件夹中的-packagetargets-文件&quot;&gt;build 文件夹中的 Package.targets 文件&lt;/h4&gt;

&lt;p&gt;下面是 build 文件夹中的 Package.targets 文件的全部内容。&lt;/p&gt;

&lt;p&gt;我们写了两个编译目标，即 Target。&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoEvaluateProperties&lt;/code&gt; 没有指定任何执行时机，但帮我们计算了两个属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRoot&lt;/code&gt; 即 NuGet 包的根目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoSourceFolder&lt;/code&gt; 即 NuGet 包中的源代码目录&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，我们添加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 任务，用于在编译期间显示一条信息，这对于调试来说非常方便。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 这个编译目标指定在 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 之前执行，并且执行需要依赖于 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoEvaluateProperties&lt;/code&gt; 编译目标。这意味着当编译执行到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 步骤时，将在它执行之前插入 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 编译目标来执行，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 依赖于 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoEvaluateProperties&lt;/code&gt;，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoEvaluateProperties&lt;/code&gt; 会插入到更之前执行。那么在微观上来看，这三个编译任务的执行顺序将是：&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoEvaluateProperties&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 中，我们定义了一个集合 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoCompile&lt;/code&gt;，集合中包含 NuGet 包源代码文件夹中的所有 .cs 文件。另外，我们又定义了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoCompile&lt;/code&gt; 集合中的所有内容添加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合中。&lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 是 .NET 项目中的一个已知集合，当 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 执行时，所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合中的文件将参与编译。注意到我没有直接将 NuGet 包中的源代码文件引入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合中，而是经过了中转。后面第五步中，你将体会到这样做的作用。&lt;/p&gt;

&lt;p&gt;我们也添加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 任务，用于在编译期间显示信息，便于调试。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvDemoRoot&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvDemoRoot&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvDemoSourceFolder&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\src\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvDemoSourceFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1. 初始化源代码包的编译属性&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 引入 C# 源码。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoIncludeSourceFiles&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvDemoCompile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvDemoSourceFolder)**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvDemoCompile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 引入源代码包中的所有源代码：@(_WalterlvDemoCompile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;这四个文件分别的作用&quot;&gt;这四个文件分别的作用&lt;/h4&gt;

&lt;p&gt;我们刚刚花了很大的篇幅教大家完成 props 和 targets 文件，那么这四个文件是做什么的呢？&lt;/p&gt;

&lt;p&gt;如果安装我们源代码包的项目使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 属性写目标框架，那么 NuGet 会自动帮我们导入 build 文件夹中的两个编译文件。如果安装我们源代码包的项目使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt;（注意复数形式）属性写目标框架，那么 NuGet 会自动帮我们导入 buildMultiTargeting 文件夹中的两个编译文件。&lt;/p&gt;

&lt;p&gt;如果你对这个属性不熟悉，请回到第一步看我们一开始创建的代码，你会看到这个属性的设置的。如果还不清楚，请阅读博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/configure-projects-to-target-multiple-platforms&quot;&gt;让一个 csproj 项目指定多个开发框架&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;体验和查看-nuget-源代码包&quot;&gt;体验和查看 NuGet 源代码包&lt;/h4&gt;

&lt;p&gt;也许你已经从本文拷贝了很多代码过去了，但直到目前我们还没有看到这些代码的任何效果，那么现在我们就可以来看看了。这可算是一个阶段性成果呢！&lt;/p&gt;

&lt;p&gt;先编译生成一下我们一直在完善的项目，我们就可以在解决方案目录的 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 目录下找到一个 NuGet 包。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-19-26-17.png&quot; alt=&quot;生成项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-19-29-22.png&quot; alt=&quot;生成的 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们要打开这个 NuGet 包看看里面的内容。你需要先去应用商店下载 &lt;a href=&quot;https://www.microsoft.com/store/productId/9WZDNCRDMDM3&quot;&gt;NuGet Package Explorer&lt;/a&gt;，装完之后你就可以开始直接双击 NuGet 包文件，也就是 nupkg 文件。现在我们双击打开看看。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-19-32-06.png&quot; alt=&quot;NuGet 包中的内容&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们的体验到此为止。如果你希望在真实的项目当中测试，可以阅读其他博客了解如何在本地测试 NuGet 包。&lt;/p&gt;

&lt;h3 id=&quot;第五步加入-wpf-项目支持&quot;&gt;第五步：加入 WPF 项目支持&lt;/h3&gt;

&lt;p&gt;截至目前，我们只是在源代码包中引入了 C# 代码。如果我们需要加入到源代码包中的代码包含 WPF 的 XAML 文件，或者安装我们源代码包的目标项目包含 WPF 的 XAML 文件，那么这个 NuGet 源代码包直接会导致无法编译通过。至于原因，你需要阅读我的另一篇博客来了解：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-wpf-assemblies-are-compiled&quot;&gt;WPF 程序的编译过程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;即便你不懂 WPF 程序的编译过程，你也可以继续完成本文的所有内容，但可能就不会明白为什么接下来我们要那样去修改我们之前创建的文件。&lt;/p&gt;

&lt;p&gt;接下来我们将修改这些文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;build 文件夹中的 Package.props 文件&lt;/li&gt;
  &lt;li&gt;build 文件夹中的 Package.targets 文件&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;build-文件夹中的-packageprops-文件-1&quot;&gt;build 文件夹中的 Package.props 文件&lt;/h4&gt;

&lt;p&gt;自微软在 .NET SDK 5.0.2 开始修复了 WPF 项目中 NuGet 代码生成器的 bug 后，已经不需要在这里新增属性了。当然，如果你想增加其他的属性则可以在这里加。&lt;/p&gt;

&lt;p&gt;关于这个 bug，详见：&lt;a href=&quot;https://github.com/dotnet/wpf/pull/3846&quot;&gt;[release/5.0] Support Source Generators in WPF projects by ryalanms · Pull Request #3846 · dotnet/wpf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;在这个文件中，我们将新增一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShouldFixNuGetImportingBugForWpfProjects&lt;/code&gt;。这是我取的名字，意为“是否应该修复 WPF 项目中 NuGet 包自动导入的问题”。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;我做一个开关的原因是怀疑我们需要针对 WPF 项目进行特殊处理是 WPF 项目自身的 Bug，如果将来 WPF 修复了这个 Bug，那么我们将可以直接通过此开关来关闭我们在这一节做的特殊处理。另外，后面我们将采用一些特别的手段来调试我们的 NuGet 源代码包，在调试项目中我们也会将这个属性设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;False&lt;/code&gt; 以关闭 WPF 项目的特殊处理。&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;&amp;lt;Project&amp;gt;
&lt;/span&gt;
    &amp;lt;PropertyGroup&amp;gt;
      &amp;lt;MSBuildAllProjects&amp;gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&amp;lt;/MSBuildAllProjects&amp;gt;

--      &amp;lt;!-- 当生成 WPF 临时项目时，不会自动 Import NuGet 中的 props 和 targets 文件，这使得在临时项目中你现在看到的整个文件都不会参与编译。
&lt;span class=&quot;gd&quot;&gt;--           然而，我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
--           WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug，也可能是刻意如此。
--           所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。--&amp;gt;
--      &amp;lt;ShouldFixNuGetImportingBugForWpfProjects Condition=&quot; '$(ShouldFixNuGetImportingBugForWpfProjects)' == '' &quot;&amp;gt;True&amp;lt;/ShouldFixNuGetImportingBugForWpfProjects&amp;gt;
&lt;/span&gt;    &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;build-文件夹中的-packagetargets-文件-1&quot;&gt;build 文件夹中的 Package.targets 文件&lt;/h4&gt;

&lt;p&gt;请按照下面的差异说明来修改你的 Package.targets 文件。实际上我们几乎删除任何代码，所以其实你可以将下面的所有内容作为你的新的 Package.targets 中的内容。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;MSBuildAllProjects&amp;gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&amp;lt;/MSBuildAllProjects&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

++    &amp;lt;PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;!-- 我们增加了一个属性，用于处理 WPF 特殊项目的源代码之前，确保我们已经收集到所有需要引入的源代码。 --&amp;gt;
++      &amp;lt;_WalterlvDemoImportInWpfTempProjectDependsOn&amp;gt;_WalterlvDemoIncludeSourceFiles&amp;lt;/_WalterlvDemoImportInWpfTempProjectDependsOn&amp;gt;
++    &amp;lt;/PropertyGroup&amp;gt;
&lt;/span&gt;
      &amp;lt;Target Name=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
        &amp;lt;PropertyGroup&amp;gt;
          &amp;lt;_WalterlvDemoRoot&amp;gt;$(MSBuildThisFileDirectory)..\&amp;lt;/_WalterlvDemoRoot&amp;gt;
          &amp;lt;_WalterlvDemoSourceFolder&amp;gt;$(MSBuildThisFileDirectory)..\src\&amp;lt;/_WalterlvDemoSourceFolder&amp;gt;

++    &amp;lt;!-- 修复旧版本的 Microsoft.NET.Sdk 中，WPF 项目不支持在临时项目中通过 NuGet 包生成源代码的问题。
&lt;span class=&quot;gi&quot;&gt;++         微软自称从 .NET 5.0.2 开始，可通过 IncludePackageReferencesDuringMarkupCompilation 属性来支持在 NuGet 包中生成源代码，该值默认为 true。
++         不过，在低版本的 .NET 中，或者用户主动设置此值为 false 时，依然需要修复此问题。
++         以下是此问题的描述：--&amp;gt;
++    &amp;lt;!-- 当生成 WPF 临时项目时，不会自动 Import NuGet 中的 props 和 targets 文件，这使得在临时项目中你现在看到的整个文件都不会参与编译。
++        然而，我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
++        WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug，也可能是刻意如此。
++        所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。--&amp;gt;
++    &amp;lt;ShouldFixNuGetImportingBugForWpfProjects Condition=&quot; '$(IncludePackageReferencesDuringMarkupCompilation)' != 'True' And '$(ShouldFixNuGetImportingBugForWpfProjects)' == '' &quot;&amp;gt;True&amp;lt;/ShouldFixNuGetImportingBugForWpfProjects&amp;gt;
&lt;/span&gt;
        &amp;lt;/PropertyGroup&amp;gt;
        &amp;lt;Message Text=&quot;1. 初始化源代码包的编译属性&quot; /&amp;gt;
      &amp;lt;/Target&amp;gt;

      &amp;lt;!-- 引入 C# 源码。 --&amp;gt;
      &amp;lt;Target Name=&quot;_WalterlvDemoIncludeSourceFiles&quot;
              BeforeTargets=&quot;CoreCompile&quot;
              DependsOnTargets=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
          &amp;lt;_WalterlvDemoCompile Include=&quot;$(_WalterlvDemoSourceFolder)**\*.cs&quot; /&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++        &amp;lt;_WalterlvDemoAllCompile Include=&quot;@(_WalterlvDemoCompile)&quot; /&amp;gt;
&lt;/span&gt;          &amp;lt;Compile Include=&quot;@(_WalterlvDemoCompile)&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;Message Text=&quot;2 引入源代码包中的所有源代码：@(_WalterlvDemoCompile)&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Message Text=&quot;2.1 引入源代码包中的所有源代码：@(_WalterlvDemoCompile)&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/Target&amp;gt;

++    &amp;lt;!-- 引入 WPF 源码。 --&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;Target Name=&quot;_WalterlvDemoIncludeWpfFiles&quot;
++            BeforeTargets=&quot;MarkupCompilePass1&quot;
++            DependsOnTargets=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
++      &amp;lt;ItemGroup&amp;gt;
++        &amp;lt;_WalterlvDemoPage Include=&quot;$(_WalterlvDemoSourceFolder)**\*.xaml&quot; /&amp;gt;
++        &amp;lt;Page Include=&quot;@(_WalterlvDemoPage)&quot; Link=&quot;%(_WalterlvDemoPage.FileName).xaml&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
++      &amp;lt;Message Text=&quot;2.2 引用 WPF 相关源码：@(_WalterlvDemoPage)&quot; /&amp;gt;
++    &amp;lt;/Target&amp;gt;
&lt;/span&gt;
++    &amp;lt;!-- 当生成 WPF 临时项目时，不会自动 Import NuGet 中的 props 和 targets 文件，这使得在临时项目中你现在看到的整个文件都不会参与编译。
&lt;span class=&quot;gi&quot;&gt;++         然而，我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
++         WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug，也可能是刻意如此。
++         所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。--&amp;gt;
++    &amp;lt;Target Name=&quot;_WalterlvDemoImportInWpfTempProject&quot;
++            AfterTargets=&quot;MarkupCompilePass1&quot;
++            BeforeTargets=&quot;GenerateTemporaryTargetAssembly&quot;
++            DependsOnTargets=&quot;$(_WalterlvDemoImportInWpfTempProjectDependsOn)&quot;
++            Condition=&quot; '$(ShouldFixNuGetImportingBugForWpfProjects)' == 'True' &quot;&amp;gt;
++      &amp;lt;ItemGroup&amp;gt;
++        &amp;lt;_GeneratedCodeFiles Include=&quot;@(_WalterlvDemoAllCompile)&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
++      &amp;lt;Message Text=&quot;3. 正在欺骗临时项目，误以为此 NuGet 包中的文件是 XAML 编译后的中间代码：@(_WalterlvDemoAllCompile)&quot; /&amp;gt;
++    &amp;lt;/Target&amp;gt;
&lt;/span&gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们增加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoImportInWpfTempProjectDependsOn&lt;/code&gt; 属性，这个属性里面将填写一个到多个编译目标（Target）的名称（多个用分号分隔），用于告知 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoImportInWpfTempProject&lt;/code&gt; 这个编译目标在执行之前必须确保执行的依赖编译目标。而我们目前的依赖目标只有一个，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 这个引入 C# 源代码的编译目标。如果你有其他考虑有引入更多 C# 源代码的编译目标，则需要把他们都加上（当然本文是不需要的）。为此，我还新增了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoAllCompile&lt;/code&gt; 集合，如果存在多个依赖的编译目标会引入 C# 源代码，则需要像 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeSourceFiles&lt;/code&gt; 一样，将他们都加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 的同时也加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoAllCompile&lt;/code&gt; 集合中。&lt;/p&gt;

&lt;p&gt;为什么可能有多个引入 C# 源代码的编译目标？因为本文我们只考虑了引入我们提前准备好的源代码放入源代码包中，而我们提到过可能涉及到动态生成 C# 源代码的需求。如果你有一两个编译目标会动态生成一些 C# 源代码并将其加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合中，那么请将这个编译目标的名称加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoImportInWpfTempProjectDependsOn&lt;/code&gt; 属性（注意多个用分号分隔），同时将集合也引入一份到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoAllCompile&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoIncludeWpfFiles&lt;/code&gt; 这个编译目标的作用是引入 WPF 的 XAML 文件，这很容易理解，毕竟我们的源代码中包含 WPF 相关的文件。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;请特别注意&lt;/strong&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性，并且将其指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;%(_WalterlvDemoPage.FileName).xaml&lt;/code&gt;。这意味着我们会把所有的 XAML 文件都当作在项目根目录中生成，如果你在其他的项目中用到了相对或绝对的 XAML 文件的路径，这显然会改变路径。但是，我们没有其他的方法来根据 XAML 文件所在的目录层级来自定指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性让其在正确的层级上，所以这里才写死在根目录中。
    &lt;ul&gt;
      &lt;li&gt;如果要解决这个问题，我们就需要在生成 NuGet 包之前生成此项目中所有 XAML 文件的正确的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性（例如改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Views\%(_WalterlvDemoPage.FileName).xaml&lt;/code&gt;），这意味着需要在此项目编译期间执行一段代码，把 Package.targets 文件中为所有的 XAML 文件生成正确的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性。本文暂时不考虑这个问题，但你可以参考 &lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard&lt;/a&gt; 项目来了解如何动态生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoPage&lt;/code&gt; 集合中转地存了 XAML 文件，这是必要的。因为这样才能正确通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 符号获取到 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileName&lt;/code&gt; 属性。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoImportInWpfTempProject&lt;/code&gt; 这个编译目标就不那么好理解了，而这个也是完美支持 WPF 项目源代码包的关键编译目标！这个编译目标指定在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 之前执行。&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 编译目标的作用是生成一个临时的项目，用于让 WPF 的 XAML 文件能够依赖同项目的 .NET 类型而编译。然而此临时项目编译期间是不会导入任何 NuGet 的 props 或 targets 文件的，这意味着我们特别添加的所有 C# 源代码在这个临时项目当中都是不存在的——如果项目使用到了我们源代码包中的源代码，那么必然因为类型不存在而无法编译通过——临时项目没有编译通过，那么整个项目也就无法编译通过。但是，我们通过在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 之间将我们源代码包中的所有源代码加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_GeneratedCodeFiles&lt;/code&gt; 集合中，即可将这些文件加入到临时项目中一起编译。而原本 &lt;code class=&quot;highlighter-rouge&quot;&gt;_GeneratedCodeFiles&lt;/code&gt; 集合中是什么呢？就是大家熟悉的 XAML 转换而成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;xxx.g.cs&lt;/code&gt; 文件。&lt;/p&gt;

&lt;h3 id=&quot;测试和发布源代码包&quot;&gt;测试和发布源代码包&lt;/h3&gt;

&lt;p&gt;现在我们再次编译这个项目，你将得到一个支持 WPF 项目的 NuGet 源代码包。&lt;/p&gt;

&lt;h2 id=&quot;完整项目结构和源代码&quot;&gt;完整项目结构和源代码&lt;/h2&gt;

&lt;p&gt;至此，我们已经完成了编写一个 NuGet 源代码包所需的全部源码。接下来你可以在项目中添加更多的源代码，这样打出来的源代码包也将包含更多源代码。由于我们将将 XAML 文件都通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性指定到根目录了，所以如果你需要添加 XAML 文件，你将只能添加到我们项目中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\src&lt;/code&gt; 目录下，除非做 &lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard&lt;/a&gt; 中类似的动态 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 生成的处理，或者在 Package.targets 文件中手工为每一个 XAML 编写一个特别的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 属性。&lt;/p&gt;

&lt;p&gt;另外，在不改变我们整体项目结构的情况下，你也可以任意添加 WPF 所需的图片资源等。但也需要在 Package.targets 中添加额外的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt; 引用。如果没有 &lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard&lt;/a&gt; 的自动生成代码，你可能也需要手工编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;接下来我会贴出更复杂的代码，用于处理更复杂的源代码包的场景。&lt;/p&gt;

&lt;h3 id=&quot;目录结构&quot;&gt;目录结构&lt;/h3&gt;

&lt;p&gt;更复杂源代码包的项目组织形式会是下面这样图这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-16-20-41-40.png&quot; alt=&quot;更复杂的源代码包项目结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们在 Assets 文件夹中新增了一个 assets 文件夹。由于资源在此项目中的路径必须和安装后的目标项目中一样才可以正确用 Uri 的方式使用资源，所以我们在项目文件 csproj 和编译文件 Package.targets 中都对这两个文件设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 到同一个文件夹中，这样才可以确保两边都能正常使用。&lt;/p&gt;

&lt;p&gt;我们在 src 文件夹的不同子文件夹中创建了 XAML 文件。按照我们前面的说法，我们也需要像资源文件一样正确在 Package.targets 中设置 Link 才可以确保 Uri 是一致的。注意，我们接下来的源代码中没有在项目文件中设置 Link，原则上也是需要设置的，就像资源一样，这样才可以确保此项目和安装此 NuGet 包中的目标项目具有相同的 XAML Uri。此例子只是因为没有代码使用到了 XAML 文件的路径，所以才能得以幸免。&lt;/p&gt;

&lt;p&gt;我们还利用了 tools 文件夹。我们在项目文件的末尾将输出文件拷贝到了 tools 目录下，这样，我们项目的 Assets 文件夹几乎与最终的 NuGet 包的文件夹结构一模一样，非常利于调试。但为了防止将生成的文件上传到版本管理，我在 tools 中添加了 .gitignore 文件：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/net*/*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;项目文件&quot;&gt;项目文件&lt;/h3&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
&lt;/span&gt;    
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;UseWpf&amp;gt;True&amp;lt;/UseWpf&amp;gt;
&lt;/span&gt;    
        &amp;lt;!-- 要求此项目编译时要生成一个 NuGet 包。--&amp;gt;
        &amp;lt;GeneratePackageOnBuild&amp;gt;true&amp;lt;/GeneratePackageOnBuild&amp;gt;
    
        &amp;lt;!-- 这里为了方便，我将 NuGet 包的输出路径设置在了解决方案根目录的 bin 文件夹下，而不是项目的 bin 文件夹下。--&amp;gt;
        &amp;lt;PackageOutputPath&amp;gt;..\bin\$(Configuration)&amp;lt;/PackageOutputPath&amp;gt;
    
        &amp;lt;!-- 创建 NuGet 包时，项目的输出文件对应到 NuGet 包的 tools 文件夹，这可以避免目标项目引用我们的 NuGet 包的输出文件。
             同时，如果将来我们准备动态生成源代码，而不只是引入静态源代码，还可以有机会运行我们 Program 中的 Main 函数。--&amp;gt;
        &amp;lt;BuildOutputTargetFolder&amp;gt;tools&amp;lt;/BuildOutputTargetFolder&amp;gt;
    
        &amp;lt;!-- 此包将不会传递依赖。意味着如果目标项目安装了此 NuGet 包，那么安装目标项目包的项目不会间接安装此 NuGet 包。--&amp;gt;
        &amp;lt;DevelopmentDependency&amp;gt;true&amp;lt;/DevelopmentDependency&amp;gt;
    
        &amp;lt;!-- 包的版本号，我们设成了一个预览版；当然你也可以设置为正式版，即没有后面的 -alpha 后缀。--&amp;gt;
        &amp;lt;Version&amp;gt;0.1.0-alpha&amp;lt;/Version&amp;gt;
    
        &amp;lt;!-- 设置包的作者。在上传到 nuget.org 之后，如果作者名与 nuget.org 上的账号名相同，其他人浏览包是可以直接点击链接看作者页面。--&amp;gt;
        &amp;lt;Authors&amp;gt;walterlv&amp;lt;/Authors&amp;gt;
    
        &amp;lt;!-- 设置包的组织名称。我当然写成我所在的组织 dotnet 职业技术学院啦。--&amp;gt;
        &amp;lt;Company&amp;gt;dotnet-campus&amp;lt;/Company&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;
    
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;!-- 我们添加的其他资源需要在这里 Link 到一个统一的目录下，以便在此项目和安装 NuGet 包的目标项目中可以用同样的 Uri 使用。 --&amp;gt;
++    &amp;lt;ItemGroup&amp;gt;
++      &amp;lt;Resource Include=&quot;Assets\assets\Icon.ico&quot; Link=&quot;Assets\Icon.ico&quot; Visible=&quot;False&quot; /&amp;gt;
++      &amp;lt;Resource Include=&quot;Assets\assets\Background.png&quot; Link=&quot;Assets\Background.png&quot; Visible=&quot;False&quot; /&amp;gt;
++    &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;      
      &amp;lt;!-- 在生成 NuGet 包之前，我们需要将我们项目中的文件夹结构一一映射到 NuGet 包中。--&amp;gt;
      &amp;lt;Target Name=&quot;IncludeAllDependencies&quot; BeforeTargets=&quot;_GetPackageFiles&quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
    
          &amp;lt;!-- 将 Package.props / Package.targets 文件的名称在 NuGet 包中改为需要的真正名称。
               因为 NuGet 包要自动导入 props 和 targets 文件，要求文件的名称必须是 包名.props 和 包名.targets；
               然而为了避免我们改包名的时候还要同步改四个文件的名称，所以就在项目文件中动态生成。--&amp;gt;
          &amp;lt;None Include=&quot;Assets\build\Package.props&quot; Pack=&quot;True&quot; PackagePath=&quot;build\$(PackageId).props&quot; /&amp;gt;
          &amp;lt;None Include=&quot;Assets\build\Package.targets&quot; Pack=&quot;True&quot; PackagePath=&quot;build\$(PackageId).targets&quot; /&amp;gt;
          &amp;lt;None Include=&quot;Assets\buildMultiTargeting\Package.props&quot; Pack=&quot;True&quot; PackagePath=&quot;buildMultiTargeting\$(PackageId).props&quot; /&amp;gt;
          &amp;lt;None Include=&quot;Assets\buildMultiTargeting\Package.targets&quot; Pack=&quot;True&quot; PackagePath=&quot;buildMultiTargeting\$(PackageId).targets&quot; /&amp;gt;
    
          &amp;lt;!-- 我们将 src 目录中的所有源代码映射到 NuGet 包中的 src 目录中。--&amp;gt;
          &amp;lt;None Include=&quot;Assets\src\**&quot; Pack=&quot;True&quot; PackagePath=&quot;src&quot; /&amp;gt;

++        &amp;lt;!-- 我们将 assets 目录中的所有源代码映射到 NuGet 包中的 assets 目录中。--&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++        &amp;lt;None Include=&quot;Assets\assets\**&quot; Pack=&quot;True&quot; PackagePath=&quot;assets&quot; /&amp;gt;
&lt;/span&gt;    
        &amp;lt;/ItemGroup&amp;gt;
      &amp;lt;/Target&amp;gt;
    
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;!-- 在编译结束后将生成的可执行程序放到 Tools 文件夹中，使得 Assets 文件夹的目录结构与 NuGet 包非常相似，便于 Sample 项目进行及时的 NuGet 包调试。 --&amp;gt;
++    &amp;lt;Target Name=&quot;_WalterlvDemoCopyOutputToDebuggableFolder&quot; AfterTargets=&quot;AfterBuild&quot;&amp;gt;
++        &amp;lt;ItemGroup&amp;gt;
++        &amp;lt;_WalterlvDemoToCopiedFiles Include=&quot;$(OutputPath)**&quot; /&amp;gt;
++        &amp;lt;/ItemGroup&amp;gt;
++        &amp;lt;Copy SourceFiles=&quot;@(_WalterlvDemoToCopiedFiles)&quot; DestinationFolder=&quot;Assets\tools\$(TargetFramework)&quot; /&amp;gt;
++    &amp;lt;/Target&amp;gt;
&lt;/span&gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;编译文件&quot;&gt;编译文件&lt;/h3&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;MSBuildAllProjects&amp;gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&amp;lt;/MSBuildAllProjects&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;!-- 我们增加了一个属性，用于处理 WPF 特殊项目的源代码之前，确保我们已经收集到所有需要引入的源代码。 --&amp;gt;
        &amp;lt;_WalterlvDemoImportInWpfTempProjectDependsOn&amp;gt;_WalterlvDemoIncludeSourceFiles&amp;lt;/_WalterlvDemoImportInWpfTempProjectDependsOn&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

      &amp;lt;Target Name=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
        &amp;lt;PropertyGroup&amp;gt;
          &amp;lt;_WalterlvDemoRoot&amp;gt;$(MSBuildThisFileDirectory)..\&amp;lt;/_WalterlvDemoRoot&amp;gt;
          &amp;lt;_WalterlvDemoSourceFolder&amp;gt;$(MSBuildThisFileDirectory)..\src\&amp;lt;/_WalterlvDemoSourceFolder&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        &amp;lt;Message Text=&quot;1. 初始化源代码包的编译属性&quot; /&amp;gt;
      &amp;lt;/Target&amp;gt;

      &amp;lt;!-- 引入主要的 C# 源码。 --&amp;gt;
      &amp;lt;Target Name=&quot;_WalterlvDemoIncludeSourceFiles&quot;
              BeforeTargets=&quot;CoreCompile&quot;
              DependsOnTargets=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
          &amp;lt;_WalterlvDemoCompile Include=&quot;$(_WalterlvDemoSourceFolder)**\*.cs&quot; /&amp;gt;
          &amp;lt;_WalterlvDemoAllCompile Include=&quot;@(_WalterlvDemoCompile)&quot; /&amp;gt;
          &amp;lt;Compile Include=&quot;@(_WalterlvDemoCompile)&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
        &amp;lt;Message Text=&quot;2.1 引入源代码包中的所有源代码：@(_WalterlvDemoCompile)&quot; /&amp;gt;
      &amp;lt;/Target&amp;gt;

      &amp;lt;!-- 引入 WPF 源码。 --&amp;gt;
      &amp;lt;Target Name=&quot;_WalterlvDemoIncludeWpfFiles&quot;
              BeforeTargets=&quot;MarkupCompilePass1&quot;
              DependsOnTargets=&quot;_WalterlvDemoEvaluateProperties&quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--        &amp;lt;_WalterlvDemoPage Include=&quot;$(_WalterlvDemoSourceFolder)**\*.xaml&quot; /&amp;gt;
--        &amp;lt;Page Include=&quot;@(_WalterlvDemoPage)&quot; Link=&quot;Views\%(_WalterlvDemoPage.FileName).xaml&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++        &amp;lt;_WalterlvDemoRootPage Include=&quot;$(_WalterlvDemoSourceFolder)FooView.xaml&quot; /&amp;gt;
++        &amp;lt;Page Include=&quot;@(_WalterlvDemoRootPage)&quot; Link=&quot;Views\%(_WalterlvDemoRootPage.FileName).xaml&quot; /&amp;gt;
++        &amp;lt;_WalterlvDemoThemesPage Include=&quot;$(_WalterlvDemoSourceFolder)Themes\Walterlv.Windows.xaml&quot; /&amp;gt;
++        &amp;lt;Page Include=&quot;@(_WalterlvDemoThemesPage)&quot; Link=&quot;Views\%(_WalterlvDemoThemesPage.FileName).xaml&quot; /&amp;gt;
++        &amp;lt;_WalterlvDemoIcoResource Include=&quot;$(_WalterlvDemoRoot)assets\*.ico&quot; /&amp;gt;
++        &amp;lt;_WalterlvDemoPngResource Include=&quot;$(_WalterlvDemoRoot)assets\*.png&quot; /&amp;gt;
++        &amp;lt;Resource Include=&quot;@(_WalterlvDemoIcoResource)&quot; Link=&quot;assets\%(_WalterlvDemoIcoResource.FileName).ico&quot; /&amp;gt;
++        &amp;lt;Resource Include=&quot;@(_WalterlvDemoPngResource)&quot; Link=&quot;assets\%(_WalterlvDemoPngResource.FileName).png&quot; /&amp;gt;
&lt;/span&gt;        &amp;lt;/ItemGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;Message Text=&quot;2.2 引用 WPF 相关源码：@(_WalterlvDemoPage);@(_WalterlvDemoIcoResource);@(_WalterlvDemoPngResource)&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Message Text=&quot;2.2 引用 WPF 相关源码：@(_WalterlvDemoRootPage);@(_WalterlvDemoThemesPage);@(_WalterlvDemoIcoResource);@(_WalterlvDemoPngResource)&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/Target&amp;gt;

      &amp;lt;!-- 当生成 WPF 临时项目时，不会自动 Import NuGet 中的 props 和 targets 文件，这使得在临时项目中你现在看到的整个文件都不会参与编译。
           然而，我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
           WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug，也可能是刻意如此。
           所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。--&amp;gt;
      &amp;lt;Target Name=&quot;_WalterlvDemoImportInWpfTempProject&quot;
              AfterTargets=&quot;MarkupCompilePass1&quot;
              BeforeTargets=&quot;GenerateTemporaryTargetAssembly&quot;
              DependsOnTargets=&quot;$(_WalterlvDemoImportInWpfTempProjectDependsOn)&quot;
              Condition=&quot; '$(ShouldFixNuGetImportingBugForWpfProjects)' == 'True' &quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
          &amp;lt;_GeneratedCodeFiles Include=&quot;@(_WalterlvDemoAllCompile)&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
        &amp;lt;Message Text=&quot;3. 正在欺骗临时项目，误以为此 NuGet 包中的文件是 XAML 编译后的中间代码：@(_WalterlvDemoAllCompile)&quot; /&amp;gt;
      &amp;lt;/Target&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;开源项目&quot;&gt;开源项目&lt;/h3&gt;

&lt;p&gt;本文涉及到的所有代码均已开源到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.PackageDemo&quot;&gt;walterlv.demo/Walterlv.PackageDemo at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多内容&quot;&gt;更多内容&lt;/h2&gt;

&lt;h3 id=&quot;sourceyard-开源项目&quot;&gt;SourceYard 开源项目&lt;/h3&gt;

&lt;p&gt;本文服务于开源项目 SourceYard，为其提供支持 WPF 项目的解决方案。&lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard: Add a NuGet package only for dll reference? By using dotnetCampus.SourceYard, you can pack a NuGet package with source code. By installing the new source code package, all source codes behaviors just like it is in your project.&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;相关博客&quot;&gt;相关博客&lt;/h3&gt;

&lt;p&gt;更多制作源代码包的博客可以阅读。从简单到复杂的顺序：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-microsoft.net.sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/issues-of-nuget-package-import-for-wpf-projects&quot;&gt;制作通过 NuGet 分发的源代码包时，如果目标项目是 WPF 则会出现一些问题（探索篇，含解决方案） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/sourceyard-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;SourceYard 制作源代码包 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 07 Jun 2021 07:14:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/build-source-code-package-for-wpf-projects.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/build-source-code-package-for-wpf-projects.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>在项目文件 / MSBuild / NuGet 包中编写扩展编译的时候，正确使用 props 文件和 targets 文件</title>
        <description>&lt;p&gt;.NET 扩展编译用的文件有 .props 文件和 .targets 文件。不给我选择还好，给了我选择之后我应该使用哪个文件来编写扩展编译的代码呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你不了解 .props 文件或者 .targets 文件，可以阅读下面的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体的例子有下面这些博客。不过大概阅读一下就好，这只是 .props 和 .targets 文件的一些应用。文章比较长，你可以考虑稍后阅读。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当我们创建的 NuGet 包中包含 .props 和 .targets 文件的时候，我们相当于在项目文件 csproj 的两个地方添加了 Import 这些文件的代码。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 本来是没有下面这一行的，我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 本来是没有下面这一行的，我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你安装的多份 NuGet 包都带有 .props 和 .targets 文件，那么就相当于帮助你 Import 了多个：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 本来是没有下面这一行的，我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 本来是没有下面这一行的，我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，什么代码写到 .props 里而什么代码写到 .targets 里就一目了然了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果你是定义属性或者为属性设置初值，那么请写到 .props 里面
    &lt;ul&gt;
      &lt;li&gt;这样，所有的 NuGet 包或者扩展的编译流程都将可以访问到你设置的属性的值&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果你是使用属性，或者按条件设置属性，那么请写到 .targets 里面
    &lt;ul&gt;
      &lt;li&gt;因为这个时候多数的属性已经初始化完毕，你可以使用到属性的值了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果你写的是编译目标（Target），那么请写到 .targets 里面
    &lt;ul&gt;
      &lt;li&gt;编译目标是扩展编译的，通常都是使用属性&lt;/li&gt;
      &lt;li&gt;也会有一些产生属性的，但那都是需要在编译期间产生的属性，其他依赖需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOn&lt;/code&gt; 等属性来获取&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;例如下面的属性适合写到 .props 里面。这是一个设置属性初始值的地方：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 当生成 WPF 临时项目时，不会自动 Import NuGet 中的 props 和 targets 文件，这使得在临时项目中你现在看到的整个文件都不会参与编译。
       然而，我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
       WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug，也可能是刻意如此。
       所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ShouldFixNuGetImportingBugForWpfProjects&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(ShouldFixNuGetImportingBugForWpfProjects)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ShouldFixNuGetImportingBugForWpfProjects&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个属性的含义你可以在我的另一篇博客中找到：&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects.html&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目）&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;而下面的属性适合写到 .targets 里面，因为这里使用到了其他的属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 因为这里使用到了 `Configuration` 属性，需要先等到此属性已经初始化完成再使用，否则我们会拿到非预期的值。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ShouldOptimizeDebugging&amp;gt;&lt;/span&gt; Condition=&quot; '$(Configuration)' == 'Debug' &quot;&amp;gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ShouldOptimizeDebugging&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 07 Jun 2021 07:12:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-msbuild-codes-into-props-or-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-msbuild-codes-into-props-or-targets.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Windows 系统上如何揪出阻止你屏幕关闭的程序</title>
        <description>&lt;p&gt;使用 Win32 API &lt;code class=&quot;highlighter-rouge&quot;&gt;SetThreadExecutionState&lt;/code&gt; 可以阻止进入屏幕保护程序，也能阻止屏幕关闭、阻止系统睡眠。这很方便，这也就可能造成各种参差不齐的程序都试图阻止你的屏幕关闭，于是来一个一整晚亮瞎眼就很难受。&lt;/p&gt;

&lt;p&gt;本文教大家如何揪出阻止你屏幕关闭的程序。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我们主要使用系统自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;powercfg&lt;/code&gt; 来查询相关的应用。因此，你需要以管理员权限打开你喜欢的终端。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;命令-powercfg-requests&quot;&gt;命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;powercfg /requests&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;在终端中输入命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;powercfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/requests&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/requests&lt;/code&gt; 参数的作用是‎“列举应用程序和驱动程序的电源请求。电源请求可防止计算机自动关闭显示屏或进入低功耗睡眠模式。‎”官方文档对此的描述是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Enumerates application and driver Power Requests. Power Requests prevent the computer from automatically powering off the display or entering a low-power sleep mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，如果有某个应用或驱动设置了阻止屏幕关闭，那么就会出现在此命令执行的结果里面。&lt;/p&gt;

&lt;p&gt;比如下面是我的例子：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SteamVR 的几个进程试图阻止屏幕关闭，另外一些进程试图阻止系统睡眠&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-08-31-42.png&quot; alt=&quot;发现 SteamVR 的电源请求&quot; /&gt;&lt;/p&gt;

&lt;p&gt;结束掉 SteamVR 后重新执行此命令，可以发现已经没有进程在阻止屏幕关闭和系统睡眠了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-08-42-38.png&quot; alt=&quot;没有程序在阻止屏幕关闭&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;命令-powercfg--energy--trace&quot;&gt;命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;powercfg -energy -trace&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;在终端中输入命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;powercfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-energy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-trace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有时，应用并没有直接阻止你的屏幕关闭，而是在一段时间之内试图不断重置睡眠计时器，这种情况，前面的命令不能完全帮助你找到问题所在，于是你需要使用这个新命令。&lt;/p&gt;

&lt;p&gt;运行这个命令，你需要等待 60 秒，就像下面这样：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;em&gt;等待期间不要碰电脑，因为鼠标和键盘事件也会影响到追踪结果！&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-08-55-06.png&quot; alt=&quot;运行 60 秒的追踪&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等待完成后，它会提示你“跟踪完成”，但不会直接告诉你任何结果。结果都存在了你个账户目录下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;energy-trace.etl&lt;/code&gt; 日志文件里面，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\energy-trace.etl&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这个文件要用事件查看器打开。&lt;/p&gt;

&lt;p&gt;第一步：右键开始按钮，选择“事件查看器”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-09-22-55.png&quot; alt=&quot;打开事件查看器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二步：操作→打开保存的日志&lt;/p&gt;

&lt;p&gt;去用户文件夹中寻找“energy-trace.etl”文件，例如“C:\Users\lvyi\energy-trace.etl”，然后打开。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-09-23-24.png&quot; alt=&quot;打开保存的日志&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第三步：在保存的日志中找到可疑记录&lt;/p&gt;

&lt;p&gt;由于日志太多（几十万条），建议右击日志选择“筛选当前日志(L)…”，在筛选器里将事件来源选成“Kernel-Power”，事件 ID 设为 63。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-13-20-08.png&quot; alt=&quot;设置筛选器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，即便我设置完成，也还有 7,852 个条目。不过这时也比较容易找到问题在哪里了。提示的是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The application or service 0x0 is attempting to update the system timer resolution to a value of 0x0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即有程序试图重置系统计时器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-13-24-13.png&quot; alt=&quot;点击其中的几条查阅&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在详细信息里，可以找到是哪个程序：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-07-13-24-55.png&quot; alt=&quot;msedge 正在重置系统计时器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，在这条记录里，是“msedge.exe”。所以，可以去 Edge 浏览器标签里找找，是否有正在播放的视频或音频等。&lt;/p&gt;

&lt;h2 id=&quot;常用阻止关闭屏幕的程序&quot;&gt;常用阻止关闭屏幕的程序&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/these-windows-applications-always-keep-display-on&quot;&gt;发现电脑屏幕总是不自动关闭？看看你是否打开了这些程序……&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-hardware/design/device-experiences/powercfg-command-line-options&quot;&gt;Powercfg command-line options - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 07 Jun 2021 05:42:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detect-which-process-is-keeping-your-screen-on-in-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detect-which-process-is-keeping-your-screen-on-in-windows.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>发现电脑屏幕总是不自动关闭？看看你是否打开了这些程序……</title>
        <description>&lt;p&gt;本文收集一些已知的导致电脑屏幕不关闭的程序。如果你发现无论你设置多短的屏幕关闭超时时间但一直都不关闭，那么可以参考本文检查是否打开了这些程序。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系统设置&quot;&gt;系统设置&lt;/h2&gt;

&lt;h3 id=&quot;电源和睡眠&quot;&gt;电源和睡眠&lt;/h3&gt;

&lt;p&gt;先检查一下你系统设置中的电源和睡眠选项，时间不应该太长。一定要先看看这里，别到时候折腾了半天发现是自己设错了就亏了……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-05-14-48-37.png&quot; alt=&quot;电源和睡眠&quot; /&gt;&lt;/p&gt;

&lt;p&gt;另外，找程序的时候，不要第一眼看过去没有就忽略它了。因为你可能像我一样有很多个桌面。最好还是用任务管理器找，不会漏掉。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-05-14-53-15.png&quot; alt=&quot;多个桌面&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;程序和游戏列表&quot;&gt;程序和游戏列表&lt;/h2&gt;

&lt;h3 id=&quot;大多数游戏&quot;&gt;大多数游戏&lt;/h3&gt;

&lt;p&gt;如果你有游戏没关，你第一个就应该怀疑它！&lt;/p&gt;

&lt;p&gt;我不想把我正在玩的游戏列举出来，因为容易过时还会暴露些什么……&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://store.steampowered.com/app/224760/FEZ/&quot;&gt;FEZ&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://store.steampowered.com/app/493200/RiME/&quot;&gt;RiME&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;应用&quot;&gt;应用&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft PowerPoint &lt;em&gt;在演示模式下&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;金山 WPS 演示 &lt;em&gt;在演示模式下&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Wallpaper Engine&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;工具&quot;&gt;工具&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;GPU-Z &lt;em&gt;只要打开就会&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;为什么我想整理这份名单&quot;&gt;为什么我想整理这份名单&lt;/h3&gt;

&lt;p&gt;因为我总是时不时发现某一天电脑屏幕一直亮着。到了晚上很刺眼的，而且费电……&lt;/p&gt;

&lt;p&gt;所以，每发现一个就补充一个好了。如果你有已知的，麻烦在评论区告诉我哟！如果看不到评论区，可以&lt;a href=&quot;https://github.com/walterlv/BlogComments/issues/28&quot;&gt;前往这里评论&lt;/a&gt;，或者&lt;a href=&quot;mailto:walter.lv@qq.com&quot;&gt;给我发邮件&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;列表之外&quot;&gt;列表之外&lt;/h2&gt;

&lt;h3 id=&quot;如何揪出阻止屏幕关闭的程序&quot;&gt;如何揪出阻止屏幕关闭的程序？&lt;/h3&gt;

&lt;p&gt;可以看我的另一篇博客：&lt;a href=&quot;/post/detect-which-process-is-keeping-your-screen-on-in-windows&quot;&gt;Windows 系统上如何揪出阻止你屏幕关闭的程序&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Jun 2021 05:39:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/these-windows-applications-always-keep-display-on.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/these-windows-applications-always-keep-display-on.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>编写你的专属 MSBuild C# 代码生成器：在保存文件时自动实时生成你的代码</title>
        <description>&lt;p&gt;我之前的博客中有介绍如何在项目中生成额外的代码，也有介绍制作一个生成代码的 NuGet 包。而本文是在此基础上更进一步，可以让生成代码变成实时的；更准确的说，是在保存文件时即生成代码，而无需完整编译一次项目。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;一天，头像全白昵称空格的“wuweilai”童鞋问我为什么 GRPC 的 NuGet 包能自动在 .proto 文件保存时更新生成的代码，怎么才能做到像它那样。然后，我研究了下 &lt;a href=&quot;https://www.nuget.org/packages/Grpc.Tools/&quot;&gt;Grpc.Tools&lt;/a&gt; 包里的代码，外加跟他反复讨论，摸清了自动生成代码的方法。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景知识&quot;&gt;背景知识&lt;/h2&gt;

&lt;p&gt;本文的知识非常简单，如果只是希望知道怎么实时生成代码的话，把本文后面的代码复制一下就可以了。但如果希望完整了解基于 MSBuild 生成代码的原理，你可以需要了解以下知识或教程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj.html&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool.html&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool.html&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;准备项目&quot;&gt;准备项目&lt;/h2&gt;

&lt;p&gt;我们创建一个全新的项目，用来了解如何实时生成代码。&lt;/p&gt;

&lt;p&gt;如下图，就是个普通的控制台应用程序。我额外生成了一个 Test.txt 文件，里面什么也没有。我们即将实现的是：&lt;strong&gt;在保存 Test.txt 文件时，会立即执行我们的编译流程&lt;/strong&gt;，这样，我们便能基于 Test.txt 来实时生成一些代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-06-01-19-54-27.png&quot; alt=&quot;一个简单的项目结构&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;最简单的自动生成代码的逻辑&quot;&gt;最简单的自动生成代码的逻辑&lt;/h2&gt;

&lt;p&gt;现在，我们打开项目 csproj 文件（双击项目名称即可打开编辑这个文件）：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

    &amp;lt;PropertyGroup&amp;gt;
      &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
      &amp;lt;TargetFramework&amp;gt;net5.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;/PropertyGroup&amp;gt;

+   &amp;lt;!-- 将项目中的所有 txt 文件搜集起来，用 WalterlvDemoFile 集合存起来。--&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;ItemGroup&amp;gt;
+     &amp;lt;WalterlvDemoFile Include=&quot;**\*.txt&quot; Generator=&quot;MSBuild:Compile&quot; /&amp;gt;
+   &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;
+   &amp;lt;!-- 注册 WalterlvDemoFile 项为一个 Item，这样它的通用属性就能被识别了。 --&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;ItemGroup&amp;gt;
+     &amp;lt;AvailableItemName Include=&quot;WalterlvDemoFile&quot; /&amp;gt;
+   &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;
+   &amp;lt;!-- 随便写一个 Target，在编译之前做些什么。 --&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;Target Name=&quot;WalterlvDemoTarget&quot; BeforeTargets=&quot;BeforeCompile&quot;&amp;gt;
+     &amp;lt;Exec Command=&quot;winver&quot; /&amp;gt;
+   &amp;lt;/Target&amp;gt;
&lt;/span&gt;
  &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我把新增的代码高亮出来了。如果你想复制到你的项目里，记得去掉行首的所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; 号。&lt;/p&gt;

&lt;p&gt;等你复制到项目里之后，试着在 Test.txt 文件里面随便写点什么，然后保存。你会发现……呃……弹出了一个 Windows 版本号窗口……&lt;/p&gt;

&lt;h2 id=&quot;最简代码解读&quot;&gt;最简代码解读&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;我们定义了一个 Target，名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoTarget&lt;/code&gt;（随便取的名字），并要求在 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeCompile&lt;/code&gt; 这个 Target 执行之前执行。
    &lt;ul&gt;
      &lt;li&gt;关于时机，可以阅读：
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;/post/extend-the-visual-studio-build-process.html&quot;&gt;通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;/post/build-multi-targeting-nuget-package.html&quot;&gt;在制作多框架项目的 NuGet 包时应该注意的问题（buildMultiTargeting/TargetFrameworks&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;我们做了一个有趣的事情，在这个 Target 里面，显示了“系统版本号”（因为我想让实时编译过程变得更直观）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;我们定义了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFile&lt;/code&gt; 项，这是随便取的名字，是为了搜集 &lt;code class=&quot;highlighter-rouge&quot;&gt;*.txt&lt;/code&gt; 文件。&lt;/li&gt;
  &lt;li&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFile&lt;/code&gt; 里指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Generator&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild:Compile&lt;/code&gt;。
    &lt;ul&gt;
      &lt;li&gt;对于已知的项（Item）来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;Generator&lt;/code&gt; 属性是 MSBuild 编译时的一个已知元数据（Metadata），其作用为当此文件改变时，会执行一个指定的 Target&lt;/li&gt;
      &lt;li&gt;我们将其指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild:Compile&lt;/code&gt;，即指定为 MSBuild 内置的一个 Target &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt;，意为执行一次编译&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;然而，&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFile&lt;/code&gt; 并不是已知的项，所以我们还需要额外将 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFile&lt;/code&gt; 添加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;AvailableItemName&lt;/code&gt; 集合里。
    &lt;ul&gt;
      &lt;li&gt;关于已知的项，可以阅读：
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items&quot;&gt;Common MSBuild Project Items - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;加完之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFile&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Generator&lt;/code&gt; 属性就可以被自动启用了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;延伸&quot;&gt;延伸&lt;/h2&gt;

&lt;p&gt;在上面那个最简的 Demo 中，我们弹出了个 Windows 版本号，这真的只是为了让你立刻注意到某个代码执行了。当然真正生成代码肯定不会是这样的弹窗。&lt;/p&gt;

&lt;p&gt;不过，你可以从我的其他博客里找到很多生成代码的方法，比如这篇……还有这篇……还有这这这篇……&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/generate-code-of-generic-types.html&quot;&gt;生成代码，从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool.html&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool.html&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package.html&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-Microsoft.NET.Sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85.html&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://mhut.ch/journal/2015/06/30/build-time-code-generation-in-msbuild&quot;&gt;Build Time Code Generation in MSBuild · mhut.ch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/grpc/grpc&quot;&gt;grpc/grpc: The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Grpc.Tools/&quot;&gt;NuGet Gallery - Grpc.Tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 01 Jun 2021 12:34:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-generate-code-when-file-is-saved.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-generate-code-when-file-is-saved.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将基于第四篇的简单场景，通过摇杆的方式控制玩家移动。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备场景&quot;&gt;准备场景&lt;/h2&gt;

&lt;p&gt;如果你是基于本系列第四篇来做的摇杆移动，那么直接开始本篇。如果是基于第五篇（传送），那么，在本文开始之前，我们需要先把第五篇里传送相关的游戏对象禁用。&lt;/p&gt;

&lt;p&gt;如下图，选择所有与传送相关的游戏对象，右键然后“切换激活状态”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-16-14-11.png&quot; alt=&quot;禁用传送相关的对象&quot; /&gt;&lt;br /&gt;
▲ 禁用传送相关的对象&lt;/p&gt;

&lt;h2 id=&quot;编写移动玩家的脚本&quot;&gt;编写移动玩家的脚本&lt;/h2&gt;

&lt;p&gt;选中“Player”，在检查器中添加组件。我们添加一个名为“PlayerMovementScript”的脚本。双击新添加的脚本文件，会用 Visual Studio 打开这个脚本文件，我们需要添加一点点的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Valve.VR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Valve.VR.InteractionSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlayerMovementScript&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SteamVR_Action_Vector2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;localMovement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worldMovement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hmdTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TransformDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;localMovement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worldMovementOfPlane&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProjectOnPlane&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worldMovement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worldMovementOfPlane&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，我们定义了两个属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SteamVR_Action_Vector2&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;input&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;speed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们现在定义的这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SteamVR_Action_Vector2&lt;/code&gt; 类型是 SteamVR 输入的一种类型，当使用 VR 控制器产生一个二维向量类型的数据时，就会生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;SteamVR_Action_Vector2&lt;/code&gt; 类型的数据。例如推动摇杆会产生这样的二维向量。我们稍后也会将这个类型绑定到摇杆上。&lt;/p&gt;

&lt;p&gt;关于 SteamVR 能产生的其他输入类型，可以参考林德熙的博客：&lt;a href=&quot;https://blog.lindexi.com/post/Unity3D-OpenVR-SteamVR-Input-Action-%E5%8A%A8%E4%BD%9C.html&quot;&gt;Unity3D OpenVR SteamVR Input Action 动作&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt; 类型则跟所有编程语言一样，只是一个浮点数而已。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 函数中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;input.axis.x&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;input.axis.y&lt;/code&gt; 是我们从 SteamVR 的二维向量中取得的 X、Y 分量；但是，我们将它转换成一个三维向量。这样，我们就能得到一个摇杆映射到三维坐标中与地面平行的平面上的坐标（相对坐标）。&lt;/li&gt;
  &lt;li&gt;这个坐标是相对坐标，而要移动玩家，我们需要一个世界坐标下的移动向量，于是我们拿头显的变换量，将这个本地坐标转换到世界坐标中。最终得到的世界坐标，我们保存到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;worldMovement&lt;/code&gt; 变量中。&lt;/li&gt;
  &lt;li&gt;为了避免让玩家移动到空中或地面以下，我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;worldMovement&lt;/code&gt; 向量投影到与地面平行的二维平面上。&lt;/li&gt;
  &lt;li&gt;最终，我们用速度、经过的时间和之前计算得到的二维平面上的世界三维坐标相乘，便得到了这一帧的移动向量，将其叠加到玩家的位置坐标上即得到了新一帧的玩家坐标。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;设置-steamvr-输入&quot;&gt;设置 SteamVR 输入&lt;/h2&gt;

&lt;p&gt;现在，回到 Unity 编辑器中，在“Player”对象的检查器中，找到我们刚刚添加的“PlayerMovementScript”脚本，我们需要设置这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;input&lt;/code&gt; 属性应该由什么进行输入。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-16-37-04.png&quot; alt=&quot;选择输入&quot; /&gt;&lt;br /&gt;
▲ 选择输入&lt;/p&gt;

&lt;p&gt;在这个下拉列表中，我们点击“Add”（添加）。我们添加一个新的（默认名字是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NewAction&lt;/code&gt;）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-18-17-05.png&quot; alt=&quot;添加新的输入&quot; /&gt;&lt;br /&gt;
▲ 添加新的输入&lt;/p&gt;

&lt;p&gt;这是一个抽象的，二维向量类型的输入，我将其取名为“DirectMovement”（意为直接移动，与之相对的是本系列第五篇说的传送移动 Teleport）。SteamVR 的这种抽象的输入可以很好地将编写代码时的输入与各种各样不同类型的 VR 控制器隔离开来，避免 VR 应用绑死某个控制器的按键。&lt;/p&gt;

&lt;p&gt;以下是我为此添加的“DirectMovement”：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;名字是“DirectMovement”&lt;/li&gt;
  &lt;li&gt;类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Vector2&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;必要性为“suggested”（suggested 表示开发者定义的，但允许用户修改的按键绑定设置；而 mandatory 表示开发者强制定义不允许用户修改的按键绑定设置）&lt;/li&gt;
  &lt;li&gt;我额外添加了中文和英文的两个不同本地化语言（这会在 SteamVR 的按键绑定设置时显示给开发者和用户看）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-18-18-09.png&quot; alt=&quot;DirectMovement 的动作设置&quot; /&gt;&lt;br /&gt;
▲ DirectMovement 的动作设置&lt;/p&gt;

&lt;p&gt;添加完成之后，点击“SteamVR Input”窗口左下角的“Save and generate”按钮，等待编译完成后，关闭这个窗口。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;特别注意&lt;/strong&gt;：&lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt; 的动作组不建议删除，因为 SteamVR 组件里很多组件都用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt; 动作组里的动作，删除会导致无法看见手和手柄。&lt;/p&gt;

&lt;p&gt;再回到“Player”游戏对象的检查器中找到“PlayerMovementScript”脚本，我们可以为输入选择我们刚刚添加的“DirectMovement”动作了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-18-20-37.png&quot; alt=&quot;选择 DirectMovement 动作&quot; /&gt;&lt;br /&gt;
▲ 选择 DirectMovement 动作&lt;/p&gt;

&lt;h2 id=&quot;设置控制器按键绑定&quot;&gt;设置控制器按键绑定&lt;/h2&gt;

&lt;p&gt;现在，我们需要重新打开“SteamVR Input”窗口来设置按键绑定。这个窗口在“窗口”-&amp;gt;“SteamVR Input”菜单里。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-16-55-54.png&quot; alt=&quot;SteamVR Input 菜单&quot; /&gt;&lt;br /&gt;
▲ SteamVR Input 菜单&lt;/p&gt;

&lt;p&gt;在这个“SteamVR Input”窗口中，选择右下角的“Open binding UI”按钮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-18-21-14.png&quot; alt=&quot;Open binding UI&quot; /&gt;&lt;br /&gt;
▲ Open binding UI&lt;/p&gt;

&lt;p&gt;稍等片刻，会打开“控制器按键设置”界面（这是 SteamVR 的界面，以后玩家去改键的时候看到的也是这个界面）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-16-58-56.png&quot; alt=&quot;控制器按键设置&quot; /&gt;&lt;br /&gt;
▲ 控制器按键设置&lt;/p&gt;

&lt;p&gt;首屏会显示这些信息：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当前正在开发的应用的按键设置（我们即将选择编辑它）&lt;/li&gt;
  &lt;li&gt;当前控制器（我用的是 Index Controller）&lt;/li&gt;
  &lt;li&gt;官方按键设置（对玩家来说，可通过这个设置还原成开发者的官方按键；而对我们开发者来说，也就是代码仓库里的那个按键设置）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们点击“编辑”以编辑当前的按键设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-18-22-12.png&quot; alt=&quot;编辑按键设置&quot; /&gt;&lt;br /&gt;
▲ 编辑按键设置&lt;/p&gt;

&lt;p&gt;将鼠标放到“Thumb Stick”上可以看到摇杆高亮了，这就是我们即将要绑定的那个按键。&lt;/p&gt;

&lt;p&gt;点击旁边的“➕”号，会弹出这个键的各种不同用法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;摇杆：像摇杆一样使用这个键，会产生 X、Y 坐标（这正好产生我们刚刚新建的动作里需要的 Vector2 类型的输入数据）&lt;/li&gt;
  &lt;li&gt;十字键：像“上”“下”“左”“右”四个按键一样使用这个键&lt;/li&gt;
  &lt;li&gt;滚动：像滚轮一样使用这个键，报告水平和垂直滚动量&lt;/li&gt;
  &lt;li&gt;径向菜单：像一个圆形菜单一样使用这个键&lt;/li&gt;
  &lt;li&gt;按键：像按键一样使用这个键，可以处理触摸、点击、按下、双击和长按。&lt;/li&gt;
  &lt;li&gt;切换按键：像切换一样使用这个键，按一下为开启，再按一下为关闭。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-17-06-26.png&quot; alt=&quot;Thumb Stick 键的不同种用法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们需要的是“摇杆”用法，因为这样才能产生我们需要的移动玩家的“Vector2”类型的输入数据。于是我们选择“摇杆”。&lt;/p&gt;

&lt;p&gt;现在，以摇杆的方式使用这个键可以产生三种不同的输入：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;点击：按下这个摇杆键时触发&lt;/li&gt;
  &lt;li&gt;触摸：摇杆键被触摸时触发&lt;/li&gt;
  &lt;li&gt;位置：推动摇杆时触发，产生位置输入（这是我们需要的输入）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-17-13-12.png&quot; alt=&quot;三种不同的输入&quot; /&gt;&lt;br /&gt;
▲ 三种不同的输入&lt;/p&gt;

&lt;p&gt;我们在“位置”上点击，在打开的新界面中，我们可以看到它产生“矢量2”类型的数据，并且还能发现我们刚刚在 Unity 编辑器中定义的“Direct Movement”动作。我们选择它。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-17-15-36.png&quot; alt=&quot;选择“Direct Movement”动作&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“✔️”来确定对这个摇杆键的设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-17-16-56.png&quot; alt=&quot;确认摇杆键的设置&quot; /&gt;&lt;br /&gt;
▲ 确认摇杆键的设置&lt;/p&gt;

&lt;p&gt;为了使我们的按键设置直接修改到我们的源代码，我们点击整个界面右下角的“替换默认按键设置”按钮，这将直接修改我们代码中的按键绑定文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-17-17-42.png&quot; alt=&quot;替换默认按键设置&quot; /&gt;&lt;br /&gt;
▲ 替换默认按键设置&lt;/p&gt;

&lt;h2 id=&quot;运行&quot;&gt;运行&lt;/h2&gt;

&lt;p&gt;现在，回到 Unity 编辑器，运行一下。可以看到，已经可以通过摇杆来控制玩家移动了。&lt;/p&gt;
</description>
        <pubDate>Mon, 17 May 2021 00:31:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-6.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-6.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将基于第三篇的简单场景，打开和关闭控制器的显示。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加脚本&quot;&gt;添加脚本&lt;/h2&gt;

&lt;p&gt;本文继续第三篇的内容。&lt;/p&gt;

&lt;p&gt;在“Player”上添加脚本。我们在“Update”中简单添加一些代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Valve.VR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Valve.VR.InteractionSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlayerDemoScript&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;showControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;showControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSkeletonRangeOfMotion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVRSkeletalMotionRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HideController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSkeletonRangeOfMotion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVRSkeletalMotionRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithoutController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，当 &lt;code class=&quot;highlighter-rouge&quot;&gt;showControllers&lt;/code&gt; 被设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 时，可以同时显示手与控制器，当设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 时，则只显示手。&lt;/p&gt;

&lt;p&gt;这里 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowController&lt;/code&gt; 是显示控制器，&lt;code class=&quot;highlighter-rouge&quot;&gt;HideController&lt;/code&gt; 是隐藏控制器。后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetSkeletonRangeOfMotion&lt;/code&gt; 是让手的骨骼动画适配控制器，如果指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WithController&lt;/code&gt; 则会在运动手指的时候握紧时只会握住控制器，而不会穿模到控制器里面；反之，握紧的时候则不考虑控制器的位置，会穿模。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/Yz0D3Ax?platform=enpc&amp;amp;channel=copylink&quot;&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-46-33.png&quot; alt=&quot;手握住控制器&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
▲ 手握住控制器&lt;/p&gt;

&lt;h2 id=&quot;运行&quot;&gt;运行&lt;/h2&gt;

&lt;p&gt;运行场景，当我们在“检查器”中勾选“showControllers”时，会在场景中看到手握住控制器。&lt;/p&gt;
</description>
        <pubDate>Sun, 16 May 2021 09:50:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-4.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-4.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将基于第四篇的简单场景，通过传送的方式控制玩家移动。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;搭建一个简单的场景&quot;&gt;搭建一个简单的场景&lt;/h2&gt;

&lt;p&gt;基于之前第四篇中我们添加的“Player”和控制器，我们这里简单打建一个场景。于是我们添加一个 3D 物体——“平面”——这足够简单。当然这不是必要的，只是会让我们后续的玩家移动看起来是踩在地面上，而不是悬在空中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-22-58.png&quot; alt=&quot;创建平面&quot; /&gt;&lt;br /&gt;
▲ 创建平面&lt;/p&gt;

&lt;p&gt;创建完记得在检查器里面将平面的位置设置到 (0,0,0)。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-26-22.png&quot; alt=&quot;设置平面的位置&quot; /&gt;&lt;br /&gt;
▲ 设置平面的位置&lt;/p&gt;

&lt;h2 id=&quot;创建传送动作&quot;&gt;创建传送动作&lt;/h2&gt;

&lt;p&gt;在资源中定位到“SteamVR”-&amp;gt;“InteractionSystem”-&amp;gt;“Teleport”-&amp;gt;“Prefabs”，找到“Teleporting”然后将它拖拽到场景中。&lt;/p&gt;

&lt;p&gt;只需要运行场景，你就能发现在推动手柄摇杆时就能传送了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-31-13.png&quot; alt=&quot;传送&quot; /&gt;&lt;br /&gt;
▲ 传送&lt;/p&gt;

&lt;p&gt;不过，我们只能做出这个传送的动作，而不会真正地传送出去，因为我们还没有传送的“目的地”。&lt;/p&gt;

&lt;h2 id=&quot;创建传送目的地&quot;&gt;创建传送目的地&lt;/h2&gt;

&lt;p&gt;依然是在“SteamVR”-&amp;gt;“InteractionSystem”-&amp;gt;“Teleport”-&amp;gt;“Prefabs”中。这次，我们拖拽“TeleportPoint”进入场景。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-34-27.png&quot; alt=&quot;拖拽 TeleportPoint 进入场景&quot; /&gt;&lt;br /&gt;
▲ 拖拽 TeleportPoint 进入场景&lt;/p&gt;

&lt;p&gt;我们拖拽多个这样的传送目的地到场景中，运行看看效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-39-59.png&quot; alt=&quot;可传送到目的地&quot; /&gt;&lt;br /&gt;
可传送到目的地&lt;/p&gt;

&lt;p&gt;在这张图片中，白色的地面让传送点很难看清，我们随便找一个非白色的材质拖拽到平面上。这里我直接使用“SteamVR”-&amp;gt;“InteractionSystem”-&amp;gt;“Teleport”-&amp;gt;“Textures”里的“TeleportArea”材质（偷懒）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-46-56.png&quot; alt=&quot;使用非白色的材质&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;创建传送目的区域&quot;&gt;创建传送目的区域&lt;/h2&gt;

&lt;p&gt;不过，只是传送到某个点的话，更像是看某场全景电影。要实现更加沉浸的体验，我们需要能让场景中的大多数地面都可被传送。&lt;/p&gt;

&lt;p&gt;创建一个新的平面，同样将位置调整到 (0,0,0)，再调整大小，将其与之前的白色“地面”重叠在一起。不过这个新的平面，我们要添加一个“TeleportArea”的脚本。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-51-55.png&quot; alt=&quot;添加脚本 Teleport Area&quot; /&gt;&lt;br /&gt;
▲ 添加脚本 Teleport Area&lt;/p&gt;

&lt;p&gt;运行看看，发现并不能传送（如下图）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-54-43.png&quot; alt=&quot;无法传送&quot; /&gt;&lt;br /&gt;
无法传送&lt;/p&gt;

&lt;p&gt;这是因为，我们创建的这两的平面完全重叠了。我们只需要将“TeleportArea”那个平面的 Y 坐标稍微向上移动一点点（例如 0.1 个单位）就可以了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-57-17.png&quot; alt=&quot;Y 移动一点点&quot; /&gt;&lt;br /&gt;
▲ Y 移动一点点&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-10-56-42.png&quot; alt=&quot;可以传送了&quot; /&gt;&lt;br /&gt;
▲ 可以传送了&lt;/p&gt;
</description>
        <pubDate>Sun, 16 May 2021 09:47:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-5.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-5.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将开发一个最简单的虚拟现实应用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-steamvr&quot;&gt;安装 SteamVR&lt;/h2&gt;

&lt;p&gt;出于性能考虑，Unity 编辑器已经把“资源商店”（Assets Store）从编辑器里面移到了浏览器。所以大家可以去 &lt;a href=&quot;https://assetstore.unity.com/&quot;&gt;https://assetstore.unity.com/&lt;/a&gt; 下载资源。&lt;/p&gt;

&lt;p&gt;搜索“Steam VR”，找到“SteamVR Plugin”插件（&lt;a href=&quot;https://assetstore.unity.com/packages/tools/integration/steamvr-plugin-32647&quot;&gt;https://assetstore.unity.com/packages/tools/integration/steamvr-plugin-32647&lt;/a&gt;），直接点“添加至我的资源”。（如果需要登录，就登录一下。）然后，顶部会弹出在 Unity 编辑器中打开的提示，选“是”就好了。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/integration/steamvr-plugin-32647&quot;&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-44-30.png&quot; alt=&quot;SteamVR Plugin&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
▲ SteamVR Plugin&lt;/p&gt;

&lt;p&gt;在 Unity 编辑器中，如果网络状况不太好，可能需要等待非常久的时间才能刷出 SteamVR 插件的导入界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-48-35.png&quot; alt=&quot;下载导入 SteamVR Plugin&quot; /&gt;&lt;br /&gt;
▲ 下载导入 SteamVR Plugin&lt;/p&gt;

&lt;p&gt;当成功显示了 SteamVR 下载界面后，点击右下角的“下载”按钮。等待下载完成后，点击“导入”按钮。这样，你的项目中就有了 SteamVR 的插件了。&lt;/p&gt;

&lt;p&gt;接下来，我们将利用 SteamVR 插件来开发我们的第一个虚拟现实应用。&lt;/p&gt;

&lt;h2 id=&quot;第一个虚拟现实应用&quot;&gt;第一个虚拟现实应用&lt;/h2&gt;

&lt;p&gt;因为我们刚刚安装了 SteamVR 插件，所以我们可以在界面的资产面板中看到“SteamVR”文件夹，定位到“SteamVR”-“InteractionSystem”-&amp;gt;“Core”-&amp;gt;“Prefabs”，找到“Player”，然后将它拖入到场景中（如图）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-51-41.png&quot; alt=&quot;拖入 Player 预制件&quot; /&gt;&lt;br /&gt;
▲ 拖入 Player 预制件&lt;/p&gt;

&lt;p&gt;然后，点击顶部的“▶”按钮开始调试你的第一个虚拟现实应用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-53-43.png&quot; alt=&quot;开始调试&quot; /&gt;&lt;br /&gt;
▲ 开始调试&lt;/p&gt;

&lt;p&gt;如果询问你还没有生成 SteamVR 的输入行为，是否要打开输入窗口时，点“是”。并在最后点击“Save and generate”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-55-07.png&quot; alt=&quot;是否打开 SteamVR 输入窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-05-22.png&quot; alt=&quot;SteamVR 输入窗口&quot; /&gt;&lt;br /&gt;
▲ SteamVR 输入窗口&lt;/p&gt;

&lt;p&gt;随后，拿起你的头戴式显示器（HMD，Head-mounted display），享受你的第一个虚拟现实应用（也许是游戏）吧！&lt;/p&gt;

&lt;p&gt;可以点开下面的视频看看我运行的效果：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/q0pQ321?platform=enpc&amp;amp;channel=copylink&quot;&gt;&lt;img src=&quot;/static/posts/2021-05-15-21-02-24.png&quot; alt=&quot;实机运行效果&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
▲ 实机运行效果&lt;/p&gt;
</description>
        <pubDate>Sun, 16 May 2021 08:51:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-2.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-2.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门三：最简单的五指交互</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将基于前两篇搭建的环境，做一个简单的五指交互。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;前提&quot;&gt;前提&lt;/h2&gt;

&lt;p&gt;你需要有一个在第二篇中做出的“Player”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-06-08.png&quot; alt=&quot;Unity 编辑器中的 Player&quot; /&gt;&lt;br /&gt;
▲ Unity 编辑器中的 Player&lt;/p&gt;

&lt;h2 id=&quot;将控制器换成手&quot;&gt;将控制器换成手&lt;/h2&gt;

&lt;p&gt;找到场景中的“Player”-&amp;gt;“SteamVRObjects”，选择“LeftHand”，在“检查器”中找到“Render Model Prefab”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-07-49.png&quot; alt=&quot;Render Model Prefab&quot; /&gt;&lt;br /&gt;
▲ Render Model Prefab&lt;/p&gt;

&lt;p&gt;将前面在“SteamVR”-&amp;gt;“InteractionSystem”-&amp;gt;“Core”-&amp;gt;“Prefabs”中的其他种类的左手拖拽到检查器的“Render Model Prefab”中。例如，我们拖入了“LeftRenderModel”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-09-25.png&quot; alt=&quot;拖入 LeftRenderModel&quot; /&gt;&lt;br /&gt;
▲ 拖入 LeftRenderModel&lt;/p&gt;

&lt;p&gt;SteamVR 中自带的其他几种控制器模型有：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-16-28.png&quot; alt=&quot;SteamVR 自带的手模型&quot; /&gt;&lt;br /&gt;
▲ SteamVR 自带的手模型&lt;/p&gt;

&lt;p&gt;同样，我们将右手也替换一个模型。&lt;/p&gt;

&lt;h2 id=&quot;运行&quot;&gt;运行&lt;/h2&gt;

&lt;p&gt;运行我们刚刚替换过手模型的场景，看看效果：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/ngGjpBG?platform=enpc&amp;amp;channel=copylink&quot;&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-21-24.png&quot; alt=&quot;RenderModel&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
▲ RenderModel&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/erJGmvD?platform=enpc&amp;amp;channel=copylink&quot;&gt;&lt;img src=&quot;/static/posts/2021-05-16-09-21-10.png&quot; alt=&quot;RenderModel Floppy&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
▲ RenderModel Floppy&lt;/p&gt;
</description>
        <pubDate>Sun, 16 May 2021 08:06:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-3.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-3.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境</title>
        <description>&lt;p&gt;在 Unity 的帮助下，虚拟现实应用的开发非常容易。不过国内竟然还是没有什么教程，所以这里就来一点入门的，适合新手。&lt;/p&gt;

&lt;p&gt;本文将搭建好虚拟现实的开发环境。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-1.html&quot;&gt;Unity OpenVR 虚拟现实入门一：安装配置 Unity + OpenVR 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-2.html&quot;&gt;Unity OpenVR 虚拟现实入门二：一个最简单的虚拟现实游戏/程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-3.html&quot;&gt;Unity OpenVR 虚拟现实入门三：最简单的五指交互&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-4.html&quot;&gt;Unity OpenVR 虚拟现实入门四：通过脚本控制手与控制器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-5.html&quot;&gt;Unity OpenVR 虚拟现实入门五：通过传送控制玩家移动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-openvr-starting-6.html&quot;&gt;Unity OpenVR 虚拟现实入门六：通过摇杆控制玩家移动&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步安装-unity-hub&quot;&gt;第一步：安装 Unity Hub&lt;/h2&gt;

&lt;p&gt;去官网 &lt;a href=&quot;https://unity.cn/releases&quot;&gt;https://unity.cn/releases&lt;/a&gt; 下载 Unity Hub。这是必要的，因为 Unity 编辑器的运行一定要先安装有 Unity Hub，且必须始终保持最新版，否则可能还会导致无法连接。&lt;/p&gt;

&lt;p&gt;下载完成后安装即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-21-39.png&quot; alt=&quot;Unity Hub 界面&quot; /&gt;&lt;br /&gt;
▲ Unity Hub 界面&lt;/p&gt;

&lt;h2 id=&quot;第二步安装最新版本的-unity-编辑器&quot;&gt;第二步：安装最新版本的 Unity 编辑器&lt;/h2&gt;

&lt;p&gt;启动 Unity Hub，然后选择左侧的“安装”，再选择右上角的“安装”选择你当前最新版本的 Unity，点“下一步”，选“安装”。然后等就好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-31-36.png&quot; alt=&quot;安装最新版本 Unity 编辑器 1/2&quot; /&gt;&lt;br /&gt;
▲ 安装最新版本 Unity 编辑器 1/2&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-33-02.png&quot; alt=&quot;安装最新版本 Unity 编辑器 2/2&quot; /&gt;&lt;br /&gt;
▲ 安装最新版本 Unity 编辑器 2/2&lt;/p&gt;

&lt;h2 id=&quot;第三步安装-xr-插件管理器&quot;&gt;第三步：安装 XR 插件管理器&lt;/h2&gt;

&lt;p&gt;在 Unity Hub 中，新建一个项目，然后等这个项目被 Unity 编辑器打开。首次创建项目或第一次打开某个项目会等很久，在恢复项目中所需的包。耐心等待吧。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-07-51-32.png&quot; alt=&quot;等待创建项目&quot; /&gt;&lt;br /&gt;
▲ 等待创建项目&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-16-07-55-15.png&quot; alt=&quot;XR 插件管理&quot; /&gt;&lt;br /&gt;
▲ XR 插件管理&lt;/p&gt;

&lt;p&gt;等你看到 Unity 编辑器的界面后，选择“编辑”-&amp;gt;“项目设置”，在打开的项目设置窗格中，拉到最下面的“XR 插件管理”然后选择它。在里面，你可以看到“安装 XR 插件管理”的按钮，点击它，然后等上几分钟到数小时，直到安装完成。安装完成后，需要重启 Unity 编辑器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-37-12.png&quot; alt=&quot;Unity 编辑器界面&quot; /&gt;&lt;br /&gt;
▲ Unity 编辑器界面&lt;/p&gt;

&lt;p&gt;重启完编辑器后，再进入到最底部的“XR Plug-in Management”则可以看到已经加载完成的“XR Plug-in Management”插件。勾选你打算适配的虚拟现实设备。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-05-15-20-40-09.png&quot; alt=&quot;XR Plug-in Management&quot; /&gt;&lt;br /&gt;
▲ XR Plug-in Management&lt;/p&gt;
</description>
        <pubDate>Sun, 16 May 2021 08:06:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-openvr-starting-1.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-openvr-starting-1.html</guid>
        
        
        <category>unity</category>
        
        <category>openvr</category>
        
      </item>
    
      <item>
        <title>WPF 制作高性能的透明背景异形窗口（使用 WindowChrome 而不要使用 AllowsTransparency=True）</title>
        <description>&lt;p&gt;在 WPF 中，如果想做一个背景透明的异形窗口，基本上都要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle=&quot;None&quot;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt; 这两个属性。如果不想自定义窗口样式，还需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Background=&quot;Transparent&quot;&lt;/code&gt;。这样的设置会让窗口变成 Layered Window，WPF 在这种类型窗口上的渲染性能是非常糟糕的。&lt;/p&gt;

&lt;p&gt;本文介绍如何使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 而不设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt; 制作背景透明的异形窗口，这可以避免异形窗口导致的低渲染性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景透明的异形窗口&quot;&gt;背景透明的异形窗口&lt;/h2&gt;

&lt;p&gt;如下是一个背景透明异形窗口的示例：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-13-16-56.png&quot; alt=&quot;示例异形窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此窗口包含很大的圆角，还包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;DropShadowEffect&lt;/code&gt; 制作的阴影效果。对于非透明窗口来说，这是不可能实现的。&lt;/p&gt;

&lt;h2 id=&quot;如何实现&quot;&gt;如何实现&lt;/h2&gt;

&lt;p&gt;要实现这种背景透明的异形窗口，需要为窗口设置以下三个属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle=&quot;None&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeMode=&quot;CanMinimize&quot;&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeMode=&quot;NoResize&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome.GlassFrameThickness=&quot;-1&quot;&lt;/code&gt; 或设置为其他较大的正数（可自行尝试设置之后的效果）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如下就是一个最简单的例子，最关键的三个属性我已经高亮标记出来了。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Window x:Class=&quot;Walterlv.Demo.MainWindow&quot;
            xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
            xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
&lt;span class=&quot;gi&quot;&gt;++          WindowStyle=&quot;None&quot; ResizeMode=&quot;CanMinimize&quot;
&lt;/span&gt;            Title=&quot;walterlv demo&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;WindowChrome.WindowChrome&amp;gt;
++          &amp;lt;WindowChrome GlassFrameThickness=&quot;-1&quot; /&amp;gt;
++      &amp;lt;/WindowChrome.WindowChrome&amp;gt;
&lt;/span&gt;        &amp;lt;Window.Template&amp;gt;
            &amp;lt;ControlTemplate TargetType=&quot;Window&quot;&amp;gt;
                &amp;lt;Border Padding=&quot;64&quot; Background=&quot;Transparent&quot;&amp;gt;
                    &amp;lt;Border CornerRadius=&quot;16&quot; Background=&quot;White&quot;&amp;gt;
                        &amp;lt;Border.Effect&amp;gt;
                            &amp;lt;DropShadowEffect BlurRadius=&quot;64&quot; /&amp;gt;
                        &amp;lt;/Border.Effect&amp;gt;
                        &amp;lt;ContentPresenter ClipToBounds=&quot;True&quot; /&amp;gt;
                    &amp;lt;/Border&amp;gt;
                &amp;lt;/Border&amp;gt;
            &amp;lt;/ControlTemplate&amp;gt;
        &amp;lt;/Window.Template&amp;gt;
        &amp;lt;Grid&amp;gt;
            &amp;lt;TextBlock FontSize=&quot;20&quot; Foreground=&quot;#0083d0&quot;
                   TextAlignment=&quot;Center&quot; VerticalAlignment=&quot;Center&quot;&amp;gt;
                &amp;lt;Run Text=&quot;欢迎访问吕毅的博客&quot; /&amp;gt;
                &amp;lt;LineBreak /&amp;gt;
                &amp;lt;Run Text=&quot;blog.walterlv.com&quot; FontSize=&quot;64&quot; FontWeight=&quot;Light&quot; /&amp;gt;
            &amp;lt;/TextBlock&amp;gt;
        &amp;lt;/Grid&amp;gt;
    &amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;网上流传的主流方法&quot;&gt;网上流传的主流方法&lt;/h2&gt;

&lt;p&gt;在网上流传的主流方法中，&lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt; 都是一个必不可少的步骤，另外也需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle=&quot;None&quot;&lt;/code&gt;。但是我一般都会极力反对大家这么做，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt; 会造成很严重的性能问题。&lt;/p&gt;

&lt;p&gt;如果你有留意到我的其他博客，你会发现我定制窗口样式的时候都在极力避开设置此性能极差的属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-style-using-window-chrome&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;性能对比&quot;&gt;性能对比&lt;/h2&gt;

&lt;p&gt;既然特别说到性能，那也是口说无凭，我们要拿出数据来说话。&lt;/p&gt;

&lt;p&gt;以下是我用来测试渲染性能所使用的例子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-preview-of-window-style.gif&quot; alt=&quot;测试性能所用的程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;相比于上面的例子来说，主要就是加了背景动画效果，这可以用来测试帧率。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Window x:Class=&quot;Walterlv.Demo.MainWindow&quot;
            xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
            xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
            WindowStyle=&quot;None&quot; ResizeMode=&quot;CanMinimize&quot;
            Title=&quot;walterlv demo&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
        &amp;lt;WindowChrome.WindowChrome&amp;gt;
            &amp;lt;WindowChrome GlassFrameThickness=&quot;-1&quot; /&amp;gt;
        &amp;lt;/WindowChrome.WindowChrome&amp;gt;
        &amp;lt;Window.Template&amp;gt;
            &amp;lt;ControlTemplate TargetType=&quot;Window&quot;&amp;gt;
                &amp;lt;Border Padding=&quot;64&quot; Background=&quot;Transparent&quot;&amp;gt;
                    &amp;lt;Border CornerRadius=&quot;16&quot; Background=&quot;White&quot;&amp;gt;
                        &amp;lt;Border.Effect&amp;gt;
                            &amp;lt;DropShadowEffect BlurRadius=&quot;64&quot; /&amp;gt;
                        &amp;lt;/Border.Effect&amp;gt;
                        &amp;lt;ContentPresenter ClipToBounds=&quot;True&quot; /&amp;gt;
                    &amp;lt;/Border&amp;gt;
                &amp;lt;/Border&amp;gt;
            &amp;lt;/ControlTemplate&amp;gt;
        &amp;lt;/Window.Template&amp;gt;
        &amp;lt;Grid&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;Rectangle x:Name=&quot;BackgroundRectangle&quot; Margin=&quot;0 16&quot; Fill=&quot;#d0d1d6&quot;&amp;gt;
++              &amp;lt;Rectangle.RenderTransform&amp;gt;
++                  &amp;lt;TranslateTransform /&amp;gt;
++              &amp;lt;/Rectangle.RenderTransform&amp;gt;
++              &amp;lt;Rectangle.Triggers&amp;gt;
++                  &amp;lt;EventTrigger RoutedEvent=&quot;FrameworkElement.Loaded&quot;&amp;gt;
++                      &amp;lt;BeginStoryboard&amp;gt;
++                          &amp;lt;BeginStoryboard.Storyboard&amp;gt;
++                              &amp;lt;Storyboard RepeatBehavior=&quot;Forever&quot;&amp;gt;
++                                  &amp;lt;DoubleAnimation Storyboard.TargetName=&quot;BackgroundRectangle&quot;
++                                                   Storyboard.TargetProperty=&quot;(UIElement.RenderTransform).(TranslateTransform.X)&quot;
++                                                   From=&quot;800&quot; To=&quot;-800&quot; /&amp;gt;
++                              &amp;lt;/Storyboard&amp;gt;
++                          &amp;lt;/BeginStoryboard.Storyboard&amp;gt;
++                      &amp;lt;/BeginStoryboard&amp;gt;
++                  &amp;lt;/EventTrigger&amp;gt;
++              &amp;lt;/Rectangle.Triggers&amp;gt;
++          &amp;lt;/Rectangle&amp;gt;
&lt;/span&gt;            &amp;lt;TextBlock FontSize=&quot;20&quot; Foreground=&quot;#0083d0&quot;
                   TextAlignment=&quot;Center&quot; VerticalAlignment=&quot;Center&quot;&amp;gt;
                &amp;lt;Run Text=&quot;欢迎访问吕毅的博客&quot; /&amp;gt;
                &amp;lt;LineBreak /&amp;gt;
                &amp;lt;Run Text=&quot;blog.walterlv.com&quot; FontSize=&quot;64&quot; FontWeight=&quot;Light&quot; /&amp;gt;
            &amp;lt;/TextBlock&amp;gt;
        &amp;lt;/Grid&amp;gt;
    &amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么性能数据表现如何呢？我们让这个窗口在 2560×1080 的屏幕上全屏渲染，得出以下数据：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;方案&lt;/th&gt;
      &lt;th&gt;WindowChrome&lt;/th&gt;
      &lt;th&gt;AllowsTransparency&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;帧率（fps）&lt;em&gt;数值越大越好，60 为最好&lt;/em&gt;&lt;/td&gt;
      &lt;td&gt;59&lt;/td&gt;
      &lt;td&gt;19&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;脏区刷新率（rects/s）&lt;em&gt;数值越大越好&lt;/em&gt;&lt;/td&gt;
      &lt;td&gt;117&lt;/td&gt;
      &lt;td&gt;38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;显存占用（MB）&lt;em&gt;数值越小越好&lt;/em&gt;&lt;/td&gt;
      &lt;td&gt;83.31&lt;/td&gt;
      &lt;td&gt;193.29&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;帧间目标渲染数（个）&lt;em&gt;数值越大越好&lt;/em&gt;&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;另外，对于显存的使用，如果我在 7680×2160 的屏幕上全屏渲染，&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 方案依然保持在 80+MB，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency&lt;/code&gt; 已经达到惊人的 800+MB 了。&lt;/p&gt;

&lt;p&gt;可见，对于渲染性能，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 制作的背景透明异形窗口性能完虐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency&lt;/code&gt; 制作的背景透明异形窗口，实际上跟完全没有设置透明窗口的性能保持一致。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-14-08-23.png&quot; alt=&quot;使用 WindowChrome 制作透明窗口的性能数据&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-14-08-45.png&quot; alt=&quot;使用 AllowsTransparency 制作透明窗口的性能数据&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此性能差异的原理解读，请参阅：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/WPF-%E4%BB%8E%E6%9C%80%E5%BA%95%E5%B1%82%E6%BA%90%E4%BB%A3%E7%A0%81%E4%BA%86%E8%A7%A3-AllowsTransparency-%E6%80%A7%E8%83%BD%E5%B7%AE%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;WPF 从最底层源代码了解 AllowsTransparency 性能差的原因&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;功能对比&quot;&gt;功能对比&lt;/h2&gt;

&lt;p&gt;既然 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 方法在性能上完虐网上流传的设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency&lt;/code&gt; 方法，那么功能呢？&lt;/p&gt;

&lt;p&gt;值得注意的是，由于在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 制作透明窗口的时候设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeMode=&quot;None&quot;&lt;/code&gt;，所以你拖动窗口在屏幕顶部和左右两边的时候，Windows 不会再帮助你最大化窗口或者靠边停靠窗口，于是你需要自行处理。不过窗口的标题栏拖动功能依然保留了下来，标题栏上的右键菜单也是可以继续使用的。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;方案&lt;/th&gt;
      &lt;th&gt;WindowChrome&lt;/th&gt;
      &lt;th&gt;AllowsTransparency&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;拖拽标题栏移动窗口&lt;/td&gt;
      &lt;td&gt;保留&lt;/td&gt;
      &lt;td&gt;自行实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;最小化最大化关闭按钮&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;拖拽边缘调整窗口大小&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;移动窗口到顶部可最大化&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
      &lt;td&gt;自行实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;拖拽最大化窗口标题栏还原窗口&lt;/td&gt;
      &lt;td&gt;保留&lt;/td&gt;
      &lt;td&gt;自行实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;移动窗口到屏幕两边可侧边停靠&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
      &lt;td&gt;自行实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;拖拽摇动窗口以最小化其他窗口&lt;/td&gt;
      &lt;td&gt;保留&lt;/td&gt;
      &lt;td&gt;自行实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;窗口打开/关闭/最小化/最大化/还原动画&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
      &lt;td&gt;丢失&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;表格中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;保留&lt;/code&gt; &lt;em&gt;表示此功能无需任何处理即可继续支持&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;自行实现&lt;/code&gt; &lt;em&gt;表示此功能已消失，但仅需要一两行代码即可补回功能&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;丢失&lt;/code&gt; &lt;em&gt;表示此功能已消失，如需实现需要编写大量代码&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，以上表格仅针对鼠标操作窗口。如果算上使用触摸来操作窗口，那么所有标记为 &lt;code class=&quot;highlighter-rouge&quot;&gt;自行实现&lt;/code&gt; 的都将变为 &lt;code class=&quot;highlighter-rouge&quot;&gt;丢失&lt;/code&gt;。因为虽然你可以一句话补回功能，但在触摸操作下各种 Bug，你解不完……&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/wpf/2017/09/12/touch-not-work-in-wpf.html&quot;&gt;WPF 程序无法触摸操作？我们一起来找原因和解决方法！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这两种实现的窗口之间还有一些功能上的区别：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;方案&lt;/th&gt;
      &lt;th&gt;WindowChrome&lt;/th&gt;
      &lt;th&gt;AllowsTransparency&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;点击穿透&lt;/td&gt;
      &lt;td&gt;在完全透明的部分点击依然点在自己的窗口上&lt;/td&gt;
      &lt;td&gt;在完全透明的部分点击会穿透到下面的其他窗口&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;然而，如果你希望在使用高性能的 WindowChrome 时也依然能点击穿透，那么你需要使用到一点点的小技巧来绕过 WPF 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;WS_EX_LAYERED&lt;/code&gt; 窗口样式的锁定。请参见：&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E5%88%B6%E4%BD%9C%E6%94%AF%E6%8C%81%E7%82%B9%E5%87%BB%E7%A9%BF%E9%80%8F%E7%9A%84%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84%E9%80%8F%E6%98%8E%E8%83%8C%E6%99%AF%E5%BC%82%E5%BD%A2%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 制作支持点击穿透的高性能的透明背景异形窗口&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sat, 16 Jan 2021 00:30:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-transparent-window-without-allows-transparency.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-transparent-window-without-allows-transparency.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32）</title>
        <description>&lt;p&gt;本文将介绍 Windows 系统中高 DPI 开发的基础知识。由于涉及到坐标转换，这种转换经常发生在计算的不知不觉中；所以无论你使用哪种 Windows 下的 UI 框架进行开发，你都需要了解这些内容，以免不断踩坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/D58kdD&quot;&gt;&lt;img src=&quot;/static/posts/2018-10-22-15-53-59.png&quot; alt=&quot;Windows 高 DPI 应用开发课件&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;各种不同的-windows-桌面-ui-框架&quot;&gt;各种不同的 Windows 桌面 UI 框架&lt;/h2&gt;

&lt;p&gt;微软主推的 Windows 桌面 UI 框架有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;UWP&lt;/li&gt;
  &lt;li&gt;WPF&lt;/li&gt;
  &lt;li&gt;Windows Forms&lt;/li&gt;
  &lt;li&gt;Win32 与 C++&lt;/li&gt;
  &lt;li&gt;DirectX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;后两者实际上并不是 UI 框架，是 UI 框架的底层不同实现。当然你单纯凭借 Win32 和 DirectX 去开发 GUI 应用也没有人拦你，只不过如果你试图只用 Win32 和 DirectX 而不进行各种 UI 组件封装的话，最终会非常痛苦的。&lt;/p&gt;

&lt;p&gt;UWP 只支持 Windows 10（当然也分不同的小版本，兼容起来有些小麻烦）。&lt;/p&gt;

&lt;p&gt;WPF 和 Windows Forms 的最新版本只支持 Windows 7 SP1 及以上系统。&lt;em&gt;如果要支持 Windows 7 和更早的系统，你需要降低 .NET Framework 的版本至 4.5.2 及以下；如果要 XP 支持，还需要到 4.0 及以下。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;对普通用户而言的-dpi-级别&quot;&gt;对普通用户而言的 DPI 级别&lt;/h2&gt;

&lt;p&gt;DPI 值有两种：系统 DPI (System DPI) 和屏幕 DPI (Monitor DPI)。自 Windows Vista 开始引入系统 DPI 概念，自 Windows 8.1 开始引入屏幕 DPI 概念。&lt;/p&gt;

&lt;p&gt;在 Windows Vista / 7 / 8 中，操作系统提供了真正的 DPI 的设置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-01-04-20-25-29.png&quot; alt=&quot;Windows 7 的 DPI 设置&quot; /&gt;&lt;br /&gt;
▲ Windows 7 的 DPI 设置（&lt;em&gt;控制面板 -&amp;gt; 外观与个性化 -&amp;gt; 显示&lt;/em&gt;）&lt;/p&gt;

&lt;p&gt;这里的设置改的就是系统的 DPI 值。&lt;/p&gt;

&lt;p&gt;Windows 7 中还额外提供了传统 Windows XP 风格 DPI 缩放比例的选项（此选项在 Windows 8 之后就删掉了），这也是在修改 DPI 值，只不过可以选择非 1/4 整数倍的 DPI 值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2021-01-04-20-26-22.png&quot; alt=&quot;自定义 DPI 设置&quot; /&gt;&lt;br /&gt;
▲ 自定义 DPI 设置&lt;/p&gt;

&lt;p&gt;自 Windows 8.1 开始，操作系统开始可以设置不同屏幕的 DPI 值了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-17-18-15-37.png&quot; alt=&quot;Windows 10 中的多个屏幕选择&quot; /&gt;&lt;br /&gt;
▲ Windows 10 中的多个屏幕选择&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-17-18-15-59.png&quot; alt=&quot;Windows 10 中针对每个屏幕的 DPI 设置&quot; /&gt;&lt;br /&gt;
▲ Windows 10 中针对每个屏幕的 DPI 设置&lt;/p&gt;

&lt;p&gt;如果用户在设置中更改了系统 DPI 值或屏幕 DPI 值，那么 Windows 系统会提示需要注销才会应用修改。&lt;/p&gt;

&lt;p&gt;对于 Windows 8.1 以下的系统，注销是必要的。因为系统 DPI 值如果不注销就不会改变，应用需要在系统重新登录后有了新的 DPI 值时才会正常根据新的系统 DPI 值进行渲染。否则就是系统进行的位图缩放。&lt;/p&gt;

&lt;p&gt;对于 Windows 8.1 及以上的系统，注销通常也是必要的。虽然屏幕 DPI 值已经更新，并且已向应用窗口发送了 Dpi Change 消息，但系统 DPI 值依然没变。应用必须处理 Dpi Change 消息才会正常渲染。如果应用不支持屏幕 DPI 感知，那么使用的就是系统 DPI 值，于是一样的会被系统进行位图缩放。&lt;/p&gt;

&lt;p&gt;但事情到 Windows 10 (1803) 之后，事情又有了转机。现在，你可以通过在设置中打开一个开关，使得无需注销，只要重新打开应用即可让此应用获取到最新的系统 DPI 的值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-28-11-03-11.png&quot; alt=&quot;Windows 10 (1803) 中新增的“不模糊”设置项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;方法是：打开“设置” -&amp;gt; “系统” -&amp;gt; “显示器” -&amp;gt; “高级缩放设置”，在“高级缩放设置”上，打开“允许 Windows 尝试修复应用，使其不模糊”。&lt;/p&gt;

&lt;p&gt;额外的，对于 Windows 8.1 及以上的系统，系统 DPI 值等于主屏在系统启动时的屏幕 DPI 值。&lt;/p&gt;

&lt;h2 id=&quot;对-windows-应用而言的-dpi-感知级别dpi-awareness&quot;&gt;对 Windows 应用而言的 DPI 感知级别（Dpi Awareness）&lt;/h2&gt;

&lt;p&gt;Windows 的 DPI 感知级别经过历代升级，已经有四种了。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;无感知 (&lt;strong&gt;Unaware&lt;/strong&gt;)
    &lt;ul&gt;
      &lt;li&gt;DPI 值就是一个常量 96。&lt;/li&gt;
      &lt;li&gt;如果在系统中设置缩放，那么就会采用位图拉伸（会模糊）。&lt;/li&gt;
      &lt;li&gt;更多信息请看本文末尾的故事。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;系统级感知 (&lt;strong&gt;System DPI Awareness&lt;/strong&gt;)
    &lt;ul&gt;
      &lt;li&gt;Vista 系统引入。&lt;/li&gt;
      &lt;li&gt;所有显示器上的应用共用这一个 DPI 值。&lt;/li&gt;
      &lt;li&gt;每个用户会话固定一个 DPI 值，修改 DPI 后不需要重启系统而只需要注销当前用户重新登录即可。&lt;/li&gt;
      &lt;li&gt;如果在设置中修改了 DPI，那么就会采用位图拉伸（会模糊）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;屏幕级感知 (&lt;strong&gt;Per-Monitor DPI Awareness&lt;/strong&gt;)
    &lt;ul&gt;
      &lt;li&gt;随 Windows 8.1 引入&lt;/li&gt;
      &lt;li&gt;应用的 DPI 值会随着所在屏幕的不同而改变。&lt;/li&gt;
      &lt;li&gt;当多个屏幕 DPI 不一样，而应用从一个屏幕切换到另一个屏幕的时候，应用会收到 DPI 改变的消息&lt;/li&gt;
      &lt;li&gt;只有应用的顶层 HWND 会收到 DPI 改变消息&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;屏幕级感知第二代 (&lt;strong&gt;Per-Monitor V2 DPI Awareness&lt;/strong&gt;)
    &lt;ul&gt;
      &lt;li&gt;随 Windows 10 (1607) 引入&lt;/li&gt;
      &lt;li&gt;应用的 DPI 值会随着所在屏幕的不同而改变。&lt;/li&gt;
      &lt;li&gt;当多个屏幕 DPI 不一样，而应用从一个屏幕切换到另一个屏幕的时候，应用会收到 DPI 改变的消息&lt;/li&gt;
      &lt;li&gt;应用的顶层和子 HWND 都会收到 DPI 改变消息&lt;/li&gt;
      &lt;li&gt;以下 UI 元素也会在 DPI 改变时缩放
        &lt;ul&gt;
          &lt;li&gt;非客户区（Non-client Area）&lt;/li&gt;
          &lt;li&gt;系统通用控件中的位图（comctl32V6）&lt;/li&gt;
          &lt;li&gt;对话框（&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createdialoga?wt.mc_id=MVP&quot;&gt;CreateDialog&lt;/a&gt;）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在 Windows 10 19H1 中，可以直接在任务管理器中查看到进程的 DPI Awareness：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-15-47-00.png&quot; alt=&quot;在任务管理器中查看 DPI Awareness&quot; /&gt;&lt;br /&gt;
▲ 在任务管理器中查看 DPI Awareness&lt;/p&gt;

&lt;p&gt;方法是在任务管理器中 Details 的标题栏右键，选择列，然后找到 DPI Awareness。&lt;/p&gt;

&lt;p&gt;可以看到，目前仅文件资源管理器是 Per-Monitor V2 的。&lt;/p&gt;

&lt;p&gt;关于在任务管理器中查看 DPI，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/view-process-info-using-task-manager&quot;&gt;Windows 系统上使用任务管理器查看进程的各项属性（命令行、DPI、管理员权限等） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;任务管理器上关于 DPI 的中文翻译也是蛮有意思的。&lt;/p&gt;

&lt;h2 id=&quot;不同-ui-框架对-dpi-的支持情况&quot;&gt;不同 UI 框架对 DPI 的支持情况&lt;/h2&gt;

&lt;h3 id=&quot;uwp&quot;&gt;UWP&lt;/h3&gt;

&lt;p&gt;UWP 当然支持最新的各种 DPI 感知级别，而且是完全支持。&lt;/p&gt;

&lt;h3 id=&quot;wpf&quot;&gt;WPF&lt;/h3&gt;

&lt;p&gt;WPF 的最新版支持最新的 DPI 感知级别，不过依然有限制：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Native WPF applications will DPI scale WPF hosted in other frameworks and other frameworks hosted in WPF do not automatically scale&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即原生 WPF 应用支持 DPI 缩放，在其他 UI 框架中的 WPF 也支持 DPI 缩放；但是 WPF 中嵌入的其他 UI 框架不支持自动 DPI 缩放。&lt;/p&gt;

&lt;p&gt;WPF 第一个版本（随 .NET Framework 3.5 发布）就已支持系统级 DPI 感知。&lt;/p&gt;

&lt;p&gt;.NET Framework 4.6.2 开始的 WPF 才开始支持屏幕级 DPI 感知。而 Per-Monitor V1 和 Per-Monitor V2 的支持在操作系统级别是兼容的，所以只需要修改 WPF 中的应用程序清单即可兼容第二代屏幕级 DPI 感知。&lt;/p&gt;

&lt;h3 id=&quot;windows-forms&quot;&gt;Windows Forms&lt;/h3&gt;

&lt;p&gt;Windows Forms 也是在 .NET Framework 4.7 才开始支持屏幕级 DPI 感知的。不过部分控件不支持自动随屏幕 DPI 切换。&lt;/p&gt;

&lt;h3 id=&quot;其他-ui-框架&quot;&gt;其他 UI 框架&lt;/h3&gt;

&lt;p&gt;原生 Win32 是支持最新 DPI 感知的，其他如 GDI/GDI+/MFC 等都不支持，除非开发者手工编写。&lt;/p&gt;

&lt;h2 id=&quot;混合-dpi-感知级别&quot;&gt;混合 DPI 感知级别&lt;/h2&gt;

&lt;p&gt;当项目足够大的时候，一个或几个项目成员可能很难了解所有的窗口逻辑。让一个进程的所有窗口开启 DPI 缩放对应用的高 DPI 迁移来说比较困难。不过好在我们可以开启混合 DPI 缩放。&lt;/p&gt;

&lt;p&gt;Windows 10 (1604) 开始引入顶级窗口（Top-level Window）级别的 DPI 感知，而 Windows 10 (1703) 开始引入每一个 HWND 的 DPI 感知，包括顶级窗口和非顶级窗口。这里的顶级窗口指的是没有父级的窗口，指的是 Parent，而不是 Owner。（实际上 API 在更早版本就引入了，这里有故事，详见本文末尾。）&lt;/p&gt;

&lt;p&gt;在创建一个窗口的前后分别调用 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-setthreaddpiawarenesscontext?wt.mc_id=MVP&quot;&gt;SetThreadDpiAwarenessContext&lt;/a&gt; 函数可以让创建的这个窗口具有单独的 DPI 感知级别。前一次是为了让窗口在创建时有一个对此线程的新的 DPI 感知级别，而后一次调用是恢复此线程的 DPI 感知级别。&lt;/p&gt;

&lt;p&gt;关于混合 DPI 感知级别的其他内容，可以阅读官网：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-improvements-for-desktop-applications?wt.mc_id=MVP&quot;&gt;Mixed-Mode DPI Scaling and DPI-aware APIs - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;微软的 Office 系列就是典型的使用了混合 DPI 感知级别的应用。在以下实验中，我组成了一个 96 DPI 的主屏和 144 DPI 的副屏，先在 96 DPI 的屏幕上截一张图，再将窗口移动到 144 DPI 的屏幕中再截一张图。&lt;/p&gt;

&lt;p&gt;Microsoft PowerPoint 使用的是系统 DPI 感知级别：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-10-10-29.png&quot; alt=&quot;96 DPI 下的主界面&quot; /&gt;&lt;br /&gt;
▲ 96 DPI 下的主界面&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-10-11-03.png&quot; alt=&quot;144 DPI 下的主界面&quot; /&gt;&lt;br /&gt;
▲ 144 DPI 下的主界面&lt;/p&gt;

&lt;p&gt;你可以通过点开图片查看原图来比较这两幅图在原图尺寸下的模糊程度。&lt;/p&gt;

&lt;p&gt;Microsoft PowerPoint 的演示页面使用的是屏幕 DPI 感知级别：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-10-13-43.png&quot; alt=&quot;96 DPI 下的演示页面&quot; /&gt;&lt;br /&gt;
▲ 96 DPI 下的演示页面&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-10-14-09.png&quot; alt=&quot;144 DPI 下的演示页面&quot; /&gt;&lt;br /&gt;
▲ 144 DPI 下的演示页面&lt;/p&gt;

&lt;p&gt;可以看到，演示页面在多屏 DPI 下是没有产生缩放的模糊，即采用了屏幕 DPI 感知级别。&lt;/p&gt;

&lt;p&gt;而以上的主界面和演示页面属于同一个进程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-10-17-29.png&quot; alt=&quot;只有一个 PowerPoint 进程&quot; /&gt;&lt;br /&gt;
▲ 只有一个 PowerPoint 进程&lt;/p&gt;

&lt;h2 id=&quot;dpi-相关的-windows-api-的迁移&quot;&gt;DPI 相关的 Windows API 的迁移&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;GetSystemMetrics      -&amp;gt;  GetSystemMetricsForDpi&lt;/li&gt;
  &lt;li&gt;AdjustWindowRectEx    -&amp;gt;  AdjustWindowRectExForDpi&lt;/li&gt;
  &lt;li&gt;SystemParametersInfo  -&amp;gt;  SystemParametersInfoForDpi&lt;/li&gt;
  &lt;li&gt;GetDpiForMonitor      -&amp;gt;  GetDpiForWindow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;关于-dpi-相关-api-变化的故事&quot;&gt;关于 DPI 相关 API 变化的故事&lt;/h2&gt;

&lt;p&gt;感谢 &lt;a href=&quot;https://www.52pojie.cn/home.php?mod=space&amp;amp;uid=512260&quot;&gt;Mouri_Naruto&lt;/a&gt;（毛利）提供的故事，API 的具体使用也可参考他的文章：&lt;a href=&quot;https://www.52pojie.cn/thread-512713-1-1.html&quot;&gt;【原创】实现每显示器高DPI识别(Per-Monitor DPI Aware)的注意事项&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;关于-windows-10&quot;&gt;关于 Windows 10&lt;/h3&gt;

&lt;p&gt;前文提到 Per-Monitor V2 是 Windows 10 (1703) 引入的，微软官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows&quot;&gt;High DPI Desktop Application Development on Windows - Win32 apps&lt;/a&gt; 也是这么写的。但实际上更早的 Windows 10 (1607) 就引入了相关 API，包括 SetThreadDpiAwarenessContext 和 PerMonitorV2 应用程序清单。并且更早的，V2 带来的非客户区缩放和子窗口 DPI 变更消息的 API 在 1507 和 1511（分别是 Windows 10 的第一和第二个正式版本）就已经有了，不过是未公开的（可参阅 &lt;a href=&quot;https://www.52pojie.cn/thread-512713-1-1.html&quot;&gt;【原创】实现每显示器高DPI识别(Per-Monitor DPI Aware)的注意事项&lt;/a&gt;）。1607 开始这两个非公开 API 不能使用了，因为换成了新的 API，参见 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest&quot;&gt;Setting the default DPI awareness for a process (Windows) - Win32 apps&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可以发现微软实际上宣称 1607 已经支持 Per-Monitor V2 了，而完整支持是在 1703。所谓的“完整”体现在这些地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;comctl32 从 1703 开始完整支持缩放（参见 &lt;a href=&quot;https://blogs.windows.com/windowsdeveloper/2016/10/24/high-dpi-scaling-improvements-for-desktop-applications-and-mixed-mode-dpi-scaling-in-the-windows-10-anniversary-update/#ioIsJTATkKKMored.97&quot;&gt;High DPI Scaling Improvements for Desktop Applications and “Mixed Mode” DPI Scaling in the Windows 10 Anniversary Update (1607) - Windows Developer Blog&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;如果你指定了 PerMonitor 但没指定 PerMonitorV2，那么 1607 默认是 PerMonitor，1703 默认是 PerMonitorV2。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;关于-windows-vista-之前的系统&quot;&gt;关于 Windows Vista 之前的系统&lt;/h3&gt;

&lt;p&gt;感谢 &lt;a href=&quot;https://www.52pojie.cn/home.php?mod=space&amp;amp;uid=512260&quot;&gt;Mouri_Naruto&lt;/a&gt;（毛利）提供的历史：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Windows Vista 之前的系统不代表就对 DPI 无感知，事实上 Windows Vista 之前的版本，大概是 Windows 98 开始就支持通过 GDI 相关的 API 获取当前系统的 DPI 值（当时 Windows Phone 之前的 Windows 移动端 OS 通过这种 API 支持 PPI 高达 280 的手机屏幕，毕竟我也算是 2008 年就入手 HTC Touch Diamond 的用户，那个屏幕的 PPI 值（PPI 280）直到 iPhone Retina 概念（PPI 320）出现后才超过）。&lt;/p&gt;

  &lt;p&gt;只是 Windows Vista 提供了对不明确表示 DPI 支持的应用的暴力缩放（通过 Desktop Window Manager 合成实现），毕竟那个时代除了手机之外，基本没有什么屏幕涉及到高 DPI。&lt;/p&gt;

  &lt;p&gt;倒是 Windows Vista 之前的系统的 DPI 修改是需要重启机器的……所以当时我作死给我的手机修改 DPI 也是要重启的（Windows CE 5.2 内核）&lt;/p&gt;

  &lt;p&gt;Vista 之前的版本，系统中设置缩放，如果你做到了 System Aware 的要求位图是不会模糊的（Vista 引入 DWM 虚拟化强制拉伸，主要是当时的引用没有做相关支持，在高 DPI 情况下会控件会变得非常小且布局大概率会乱掉）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows?wt.mc_id=MVP&quot;&gt;High DPI Desktop Application Development on Windows - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/WPF-Samples/blob/master/PerMonitorDPI/Developer%20Guide%20-%20Per%20Monitor%20DPI%20-%20WPF%20Preview.docx&quot;&gt;WPF-Samples/Developer Guide - Per Monitor DPI - WPF Preview.docx at master · Microsoft/WPF-Samples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/zh-cn/help/4091364/windows-10-fix-blurry-apps&quot;&gt;在 Windows 10 中修复显示模糊的应用 - Windows Help&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/en-us/help/4091364/windows-10-fix-blurry-apps&quot;&gt;Fix apps that appear blurry in Windows 10 - Windows Help&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Jan 2021 12:33:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-high-dpi-development.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-high-dpi-development.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>适合 .NET 开发者用的 GitHub Actions（时不时更新）</title>
        <description>&lt;p&gt;本文制作并长期更新适合 .NET 开发者用的 GitHub Actions。整理方式为整个文件而不是单个可用的模块，这样可以方便大家以最快的速度为自己的项目添加 GitHub Actions。当然自己改改也可。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;net-编译与单元测试全平台&quot;&gt;.NET 编译与单元测试（全平台）&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：编译你的 .NET 项目，并进行单元测试。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用&lt;/strong&gt;：如果你的项目是纯 .NET 项目，无论项目是 .NET Core 还是 .NET Framework，无论是 Asp.NET Core 还是 WPF / Windows Forms，都可以用这个文件来编译和单元测试。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要求&lt;/strong&gt;：仓库的根目录有且仅有一个 sln 文件，且这个文件包含了所有重要的项目和单元测试。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.NET Build &amp;amp; Test&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;windows-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build --configuration $env:Configuration&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.configuration }}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Test&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet test --configuration $env:Configuration&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.configuration }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;net-编译与单元测试仅限-windows-系统下的编译&quot;&gt;.NET 编译与单元测试（仅限 Windows 系统下的编译）&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：编译你的 .NET 项目，并进行单元测试。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用&lt;/strong&gt;：如果你的项目是纯 .NET 项目，无论项目是 .NET Core 还是 .NET Framework，无论是 Asp.NET Core 还是 WPF / Windows Forms，都可以用这个文件来编译和单元测试。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要求&lt;/strong&gt;：仓库的根目录有且仅有一个 sln 文件，且这个文件包含了所有重要的项目和单元测试。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.NET Build &amp;amp; Test&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;windows-latest&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;fetch-depth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 安装 .NET Core&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install .NET Core&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3.1.202&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 添加 MSBuild.exe 到环境变量: https://github.com/microsoft/setup-msbuild&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Add msbuild to PATH&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;microsoft/setup-msbuild@v1.0.2&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 安装 NuGet（如果后面需要，可以使用它）&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup NuGet&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nuget/setup-nuget@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nuget-api-key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.NuGetAPIKey }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nuget-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;5.x'&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 编译整个项目&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build the solution&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;msbuild /p:Configuration=$env:Configuration -restore&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.configuration }}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 执行单元测试&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Execute unit tests&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet test -c $env:Configuration&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.configuration }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这个文件中：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们测试编译了 DEBUG 和 Release 两个不同的环境&lt;/li&gt;
  &lt;li&gt;我们使用的是 msbuild 来编译，因为这样对旧项目的兼容性最好，当然也就失去了跨平台的能力&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;nuget-push&quot;&gt;NuGet Push&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：如果你的项目是要推送 NuGet 包的，那么可以使用此工作流推送 NuGet 包。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用&lt;/strong&gt;：任何 .NET 项目。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要求&lt;/strong&gt;：仓库的根目录有且仅有一个 sln 文件。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NuGet Push&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;windows-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pack&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build --configuration Release&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Push&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet nuget push .\bin\Release\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NuGetAPIKey }} --skip-duplicate --no-symbols &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于最后的那个参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;，很魔性，只要有任何一个值都行。参见：&lt;a href=&quot;https://github.com/NuGet/Home/issues/4864&quot;&gt;dotnet nuget push - Missing value for option · Issue #4864 · NuGet/Home&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;自带环境&quot;&gt;自带环境&lt;/h2&gt;

&lt;p&gt;GitHub Actions 自带了很多环境可以用，这些自带的环境不需要再去配了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md&quot;&gt;virtual-environments/Windows2019-Readme.md at master · actions/virtual-environments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 03 Dec 2020 09:44:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/github-actions-for-dotnet-developers.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/github-actions-for-dotnet-developers.html</guid>
        
        
        <category>dotnet</category>
        
        <category>github</category>
        
      </item>
    
      <item>
        <title>各个版本 Windows 10 的名称、完整版本号、开发代号和系统自带的 .NET Framework 版本</title>
        <description>&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Windows 10 名称&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Windows 版本&lt;/th&gt;
      &lt;th&gt;开发代号&lt;/th&gt;
      &lt;th&gt;自带的 .NET Framework 版本&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;预览中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;预览中&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;October 2020 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.19042 (20H2)&lt;/td&gt;
      &lt;td&gt;20H2&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;May 2020 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.19041 (2004)&lt;/td&gt;
      &lt;td&gt;20H1&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;November 2019 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.18363 (1909)&lt;/td&gt;
      &lt;td&gt;19H2&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 May 2019 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.18362 (1903)&lt;/td&gt;
      &lt;td&gt;19H1&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 October 2018 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.17763 (1809)&lt;/td&gt;
      &lt;td&gt;RS5&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 April 2018 Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.17134 (1803)&lt;/td&gt;
      &lt;td&gt;RS4&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 Fall Creators Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.16299 (1709)&lt;/td&gt;
      &lt;td&gt;RS3&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 Creators Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.15063 (1703)&lt;/td&gt;
      &lt;td&gt;RS2&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 Anniversary Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.14393 (1607)&lt;/td&gt;
      &lt;td&gt;RS1&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.6.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10 November Update&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.10586 (1511)&lt;/td&gt;
      &lt;td&gt;TH2&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.6.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows 10&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.10240 (1507)&lt;/td&gt;
      &lt;td&gt;TH1&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.6&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Windows Server 名称&lt;/th&gt;
      &lt;th&gt;自带的 .NET Framework 版本&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows Server 1803&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows Server 1709&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.7.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Windows Server 2016&lt;/td&gt;
      &lt;td&gt;.NET Framework 4.6.2&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed?wt.mc_id=MVP&quot;&gt;How to: Determine which .NET Framework versions are installed -Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://technet.microsoft.com/en-us/windows/release-info.aspx&quot;&gt;Windows 10 release information - current branch, build history&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_10_version_history&quot;&gt;Windows 10 version history - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.thecollectionbook.info/builds/windows&quot;&gt;Builds • The Collection Book&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 30 Nov 2020 06:55:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/embeded-dotnet-version-in-all-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/embeded-dotnet-version-in-all-windows.html</guid>
        
        <category>Windows</category>
        
        <category>.Net</category>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 Xamarin 在 iOS 真机上部署应用进行调试</title>
        <description>&lt;p&gt;虽然 Xamarin 可以在 Windows 操作系统上编写和调试，但如果开发 iOS 应用，那么我们依然需要一台安装有 XCode 和 Visual Studio for Mac 的 Mac 电脑。做真机部署不是像平时使用太阳系第一 IDE Visual Studio 那样方便。&lt;/p&gt;

&lt;p&gt;所以本文需要介绍如何使用 Xamarin 在 iOS 真机上部署应用进行调试，然后顺便说一些注意事项。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备一台-mac-电脑&quot;&gt;准备一台 Mac 电脑&lt;/h2&gt;

&lt;p&gt;如果你没有 Mac 电脑，那我只能很不幸地告诉你：本文读下去已经没有什么用了，你不会成功的……当然你也可以考虑使用 Mac OS 虚拟机，但成功率太低，本文不会涉及。&lt;/p&gt;

&lt;p&gt;在 Mac 电脑上安装以下两款必备应用：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;XCode：从苹果应用商店安装&lt;/li&gt;
  &lt;li&gt;Visual Studio for Mac：在这里下载 &lt;a href=&quot;https://visualstudio.microsoft.com/vs/mac/&quot;&gt;https://visualstudio.microsoft.com/vs/mac/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这两款应用的体积都很大，如果你没有很好的网络代理设置，安装一整天都是可能的。所以还是强烈建议你有一个稳定的代理网络来下载。&lt;/p&gt;

&lt;p&gt;本文接下来的内容都假设你已经安装好了这两款应用。&lt;/p&gt;

&lt;h2 id=&quot;背景知识&quot;&gt;背景知识&lt;/h2&gt;

&lt;p&gt;你需要知道一些背景知识，不然后面真机部署的时候失败了都不知道怎么回事。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你的账号必须是苹果开发者账号
    &lt;ul&gt;
      &lt;li&gt;只需要注册 &lt;a href=&quot;https://developer.apple.com/register/&quot;&gt;Apple Developer Portal&lt;/a&gt;，不需要注册 Apple Developer Program&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;只有 XCode 才能生成开发者的 provisioning profiles&lt;/li&gt;
  &lt;li&gt;只有 XCode 才能在 iOS 真机上部署全新的应用&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;也就是说，你必须有一些操作是在 XCode 中完成；只使用 Visual Studio for Mac 是无法完成部署任务的。&lt;/p&gt;

&lt;h2 id=&quot;在-xcode-中准备&quot;&gt;在 XCode 中准备&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 XCode 中新建一个空白 iOS 项目（什么类型都可以），这个项目随时可以丢弃。&lt;/li&gt;
  &lt;li&gt;选择你新建的项目，会出现这个项目的信息可以填，默认在 General 标签中。&lt;/li&gt;
  &lt;li&gt;*[重要] 修改 Bundle Identifier。
    &lt;ul&gt;
      &lt;li&gt;将这个 Bundle Identifier 修改为你希望部署的应用的 Bundle Identifier。比如你在 Xamarin 的 Info.plist 中写的 Bundle Identifier 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;com.walterlv.CloudKeyboard&lt;/code&gt;，那么这里也必须写 &lt;code class=&quot;highlighter-rouge&quot;&gt;com.walterlv.CloudKeyboard&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;*[重要] 一定要让这个 Bundle Identifier 文本框失焦（比如按下 Tab 或在其他文本框中点一下）。
    &lt;ul&gt;
      &lt;li&gt;这个时候下面的 Signing Certificate 会出现一个加载中的动画，大概持续不到一秒钟，就会生成 iPhone Developer 的信息，这个就是包含 provisioning profiles 的信息（可以在 Provisioning Profile 旁边的感叹号中看到详细信息）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;在 Mac 上插入你的 iPhone，解锁 iPhone，等待左上角出现你 iPhone 的名称和图标。&lt;/li&gt;
  &lt;li&gt;点击 XCode 左上角的运行按钮，等待这个空白的应用部署到你的手机上。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;更新&lt;/strong&gt;：XCode 新版本的界面布局和本文略有不同，可前往以下链接查看变化：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Xamarin-iOS-%E5%88%87%E6%8D%A2%E5%BC%80%E5%8F%91%E8%80%85%E8%B4%A6%E5%8F%B7%E4%B9%8B%E5%90%8E%E7%9A%84%E7%AD%BE%E5%90%8D%E6%A0%87%E8%AF%86%E5%92%8C%E9%A2%84%E9%85%8D%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E6%9B%B4%E6%96%B0%E6%96%B9%E6%B3%95.html&quot;&gt;Xamarin iOS 切换开发者账号之后的签名标识和预配配置文件更新方法&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Xamarin-iOS-%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE%E6%8F%90%E7%A4%BA-Failed-to-register-bundle-identifier-%E5%A4%B1%E8%B4%A5.html&quot;&gt;Xamarin iOS 部署项目提示 Failed to register bundle identifier 失败&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-20-51-52.png&quot; alt=&quot;在 XCode 中进行设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;*[重要] 额外的，如果你开发的是 iOS 扩展，有两个或者更多的包，那么你需要重复步骤 3 到 6。也就是不断地修改 Bundle Identifier，等待生成新的 Developer 信息，然后部署这个空的应用&lt;/p&gt;

&lt;h2 id=&quot;在-visual-studio-for-mac-中部署&quot;&gt;在 Visual Studio for Mac 中部署&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;*[重要] 请回到你的 iPhone 手机，删除刚刚部署的应用
    &lt;ul&gt;
      &lt;li&gt;如果你刚刚部署了多个空白应用，那么都要删除&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;回到 Visual Studio for Mac 并打开你的 Xamarin 项目，然后打开准备部署的应用的 Info.plist 文件&lt;/li&gt;
  &lt;li&gt;检查 Bundle Identifier，一定要确认跟前面 XCode 中填入的是同一个 Bundle Identifier
    &lt;ul&gt;
      &lt;li&gt;额外的，如果你是开发 iOS 扩展，有两个或更多包，那么每个包都需要进入 Info.plist 文件检查 Bundle Identifier&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;点击 Bundle Signing Options，选择刚刚使用 XCode 生成的开发者信息（如果你看不到，那么就是前面 XCode 的步骤没有执行正确）&lt;/li&gt;
  &lt;li&gt;在 Mac 上插入你的 iPhone，解锁 iPhone，等待左上角出现你 iPhone 的名称和图标。
    &lt;ul&gt;
      &lt;li&gt;
        &lt;table&gt;
          &lt;tbody&gt;
            &lt;tr&gt;
              &lt;td&gt;如果没有出现，你可能需要点击一下 Debug&lt;/td&gt;
              &lt;td&gt;iPhone 区域，一定要确保选中了 iPhone 而不是 iPhone Simulator&lt;/td&gt;
            &lt;/tr&gt;
          &lt;/tbody&gt;
        &lt;/table&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;点击 Visual Studio for Mac 左上角的运行按钮，等待你 Xamarin 的应用部署到你的手机上（可能需要数十秒到数分钟）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-21-01-09.png&quot; alt=&quot;检查 Bundle Identifier&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-21-06-50.png&quot; alt=&quot;设置 Bundle Signing Options&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-21-10-28.png&quot; alt=&quot;运行与部署&quot; /&gt;&lt;/p&gt;

&lt;p&gt;理论上经过以上步骤，你就可以在你的 iPhone 上看到你用 Xamarin 开发的应用了。但其实是无法运行的。&lt;/p&gt;

&lt;p&gt;如果部署过程中发生了任何错误，请：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;检查你的步骤与本文是否有出入；&lt;/li&gt;
  &lt;li&gt;参考：&lt;a href=&quot;/post/tips-for-developing-xamarin-ios-app&quot;&gt;使用 Xamarin 开发 iOS 应用中需要注意的若干个问题&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;在-iphone-上操作&quot;&gt;在 iPhone 上操作&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;打开设置 -&amp;gt; 通用 -&amp;gt; 设备管理&lt;/li&gt;
  &lt;li&gt;点开 [自己的开发者账号]，点击 [信任]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你是首次进行此操作（实际上阅读本文操作的应该也就是首次了），那么信任自己的开发者账号可能会花比较长的时间，Visual Studio for Mac 的部署调试可能会因为等待超时而调试失败。不过这不重要，你只需要在 Visual Studio for Mac 上点击停止调试，然后再次重来就可以了。&lt;/p&gt;

&lt;p&gt;还需要注意，如果你删除了你部署的应用，那么下次部署的时候在 iPhone 上的操作部分需要重新进行。&lt;/p&gt;

&lt;p&gt;还需要注意，可能每过 6 天，本文所述的所有步骤都需要重新进行一遍。&lt;/p&gt;
</description>
        <pubDate>Sat, 31 Oct 2020 01:00:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deploy-and-debug-ios-app-using-xamarin.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deploy-and-debug-ios-app-using-xamarin.html</guid>
        
        
        <category>xamarin</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>ios</category>
        
      </item>
    
      <item>
        <title>设置进程的 RedirectStandardOutput 重定向输出后，如果不将输出读出来，会卡死此进程</title>
        <description>&lt;p&gt;设置进程的 RedirectStandardOutput 重定向输出后，必须将其读出来。本文带你做一个实验并得出结论。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重定向输出&quot;&gt;重定向输出&lt;/h2&gt;

&lt;p&gt;一个简单的尝试重定向输出的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StartInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.Output.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;UseShellExecute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CreateNoWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;正常跑起来的话不会出什么问题。不过对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.exe&lt;/code&gt; 那个进程来说，就比较危险了……&lt;/p&gt;

&lt;h2 id=&quot;卡死&quot;&gt;卡死！&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.Output.exe&lt;/code&gt; 是什么程序呢？自己写的测试程序，如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Output&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] Console.WriteLine();&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用 Visual Studio 附加到两个进程后，点击“暂停”按钮，会发现&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-08-03-19-42-13.png&quot; alt=&quot;暂停按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-08-03-19-43-04.png&quot; alt=&quot;已停止&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 循环并没有像平常的其他循环一样瞬间炸裂，而是停在了一个神奇的数字“128”上。点击“继续”按钮，过一会儿再点击“暂停”，依然显示的是“128”。&lt;/p&gt;

&lt;p&gt;说明–&lt;strong&gt;现在卡死了&lt;/strong&gt;！&lt;/p&gt;

&lt;h2 id=&quot;缓冲区已满&quot;&gt;缓冲区已满&lt;/h2&gt;

&lt;p&gt;因为我们前面的代码使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadLine()&lt;/code&gt; 等待用户输入，我们在下一行打一个断点，可以在按下回车后进入断点，于是可以观察到 &lt;code class=&quot;highlighter-rouge&quot;&gt;process&lt;/code&gt; 里面的各种字段和属性。&lt;/p&gt;

&lt;p&gt;可以注意到，&lt;code class=&quot;highlighter-rouge&quot;&gt;StandardOutput&lt;/code&gt; 属性中是存在缓冲区的，大小只有 4096 字节。打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;charBuffer&lt;/code&gt; 字段，可观察到每一个字节的值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-08-04-10-55-11.png&quot; alt=&quot;缓冲区数据&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们的输出程序，总共输出 128 次即死掉，而每次输出的行（就是那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;[      1] Console.WriteLine();&lt;/code&gt;）我正好安排到 32 个字符。乘起来刚好 4096 大小。&lt;/p&gt;

&lt;h2 id=&quot;开发注意&quot;&gt;开发注意&lt;/h2&gt;

&lt;p&gt;如果你重定向了输出流，那么一定记得取出输出数据，否则会导致被启动的程序卡死在下一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine&lt;/code&gt; 中。&lt;/p&gt;
</description>
        <pubDate>Tue, 04 Aug 2020 03:00:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/standard-output-must-be-read-if-you-redirect-standard-output.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/standard-output-must-be-read-if-you-redirect-standard-output.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>三种方法设置 .NET/C# 项目的编译顺序，而不影响项目之间的引用</title>
        <description>&lt;p&gt;当 A 项目引用 B 项目，那么使用 Visual Studio 或者 MSBuild 编译 A 项目之前就会确保 B 项目已经编译完毕。通常我们指定这种引用是因为 A 项目确实在运行期间需要 B 项目生成的程序集。&lt;/p&gt;

&lt;p&gt;但是，现在 B 项目可能仅仅只是一个工具项目，或者说 A 项目编译之后的程序集并不需要 B，仅仅只是将 B 打到一个包中，那么我们其实需要的仅仅是 B 项目先编译而已。&lt;/p&gt;

&lt;p&gt;本文介绍如何影响项目的编译顺序，而不带来项目实际引用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;方法一设置-referenceoutputassembly&quot;&gt;方法一：设置 ReferenceOutputAssembly&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Analyzer\Walterlv.Demo.Analyzer.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Build\Walterlv.Demo.Build.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详见 &lt;a href=&quot;/post/reference-a-project-without-referencing-output-assembly&quot;&gt;通过 ReferenceOutputAssembly=False 在引用项目时，不额外引入依赖文件 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;方法二设置解决方案级别的项目依赖&quot;&gt;方法二：设置解决方案级别的项目依赖&lt;/h2&gt;

&lt;p&gt;此方法可能会是更加常用的方法，但兼容性不那么好，可能在部分旧版本的 Visual Studio 或者 .NET Core 版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令下不容易工作起来。&lt;/p&gt;

&lt;p&gt;在解决方案上右键，然后选择“设置项目依赖”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-12-17-34.png&quot; alt=&quot;设置项目依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在弹出的项目依赖对话框中选择一个项目的依赖：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-12-18-39.png&quot; alt=&quot;选择项目依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;详见：&lt;a href=&quot;/post/setup-project-dependencies-in-the-solution-file&quot;&gt;通过设置 sln 解决方案依赖，确保不引用的两个项目之间有明确的编译顺序 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;方法三使用-msbuild-编译任务来编译其他项目&quot;&gt;方法三：使用 MSBuild 编译任务来编译其他项目&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BuildTheCompilerProject&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuild&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Projects=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Packages.Compiler\Walterlv.Packages.Compiler.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Properties=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Configuration=$(Configuration);Platform=$(Platform)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详见 &lt;a href=&quot;/post/msbuild-another-project-in-msbuild-targets&quot;&gt;Visual Studio 在编译 A 项目时，确保 B 项目已编译 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用哪一种&quot;&gt;使用哪一种？&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;ReferenceOutputAssembly&lt;/th&gt;
      &lt;th&gt;解决方案依赖&lt;/th&gt;
      &lt;th&gt;MSBuild 编译任务&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;位置&lt;/td&gt;
      &lt;td&gt;项目文件（csproj）或&lt;br /&gt;编译文件（&lt;code class=&quot;highlighter-rouge&quot;&gt;*.props&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;*.targets&lt;/code&gt;）&lt;/td&gt;
      &lt;td&gt;解决方案文件（sln）&lt;/td&gt;
      &lt;td&gt;项目文件（csproj）或&lt;br /&gt;编译文件（&lt;code class=&quot;highlighter-rouge&quot;&gt;*.props&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;*.targets&lt;/code&gt;）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;编译项目&lt;/td&gt;
      &lt;td&gt;✔️生效&lt;/td&gt;
      &lt;td&gt;❌无效&lt;/td&gt;
      &lt;td&gt;✔️生效&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;编译解决方案&lt;/td&gt;
      &lt;td&gt;✔️生效&lt;/td&gt;
      &lt;td&gt;✔️生效&lt;/td&gt;
      &lt;td&gt;✔️生效&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;拷贝依赖项目的输出文件&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
      &lt;td&gt;⚠是&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;要求匹配目标框架&lt;br /&gt;TargetFramework&lt;/td&gt;
      &lt;td&gt;⚠是&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;占用一个编译时机&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
      &lt;td&gt;⚠是&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;兼容性&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;⚠早期版本的&lt;br /&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 不支持&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;✔️优势&lt;br /&gt;
❌劣势&lt;br /&gt;
⚠可能优可能劣（但在本文场景是劣势）&lt;/p&gt;

&lt;p&gt;位置：代码可以写到哪些文件中&lt;br /&gt;
编译项目：使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令来编译时，传入项目文件&lt;br /&gt;
编译解决方案：使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令来编译时，传入解决方案文件&lt;br /&gt;
拷贝依赖项目的输出文件：如果 A 项目引用 B 项目，那么 B 项目的输出文件会被自动拷贝到 A 项目的输出目录中&lt;br /&gt;
要求匹配目标框架：必须匹配的框架才能引用，例如 net48 能引用 net45，netcoreapp3.1 能引用 netstandard2.0，但 net45 不能引用 netcoreapp3.1&lt;br /&gt;
占用一个编译时机：在此编译时机之前的依赖是无效的（详见：&lt;a href=&quot;/post/msbuild-another-project-in-msbuild-targets&quot;&gt;Visual Studio 在编译 A 项目时，确保 B 项目已编译&lt;/a&gt;）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/5774449/6233938&quot;&gt;Question about Visual Studio *.sln file format - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 24 Jun 2020 01:40:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/affects-project-building-order.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/affects-project-building-order.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>通过设置 sln 解决方案依赖，确保不引用的两个项目之间有明确的编译顺序</title>
        <description>&lt;p&gt;有时在编译解决方案的时候，希望两个项目有明确的编译顺序，而不是自动决定，或者在并行编译的时候同时编译。&lt;/p&gt;

&lt;p&gt;本文介绍通过设置 sln 解决方案依赖来解决编译顺序问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;设置解决方案级别的项目依赖&quot;&gt;设置解决方案级别的项目依赖&lt;/h2&gt;

&lt;p&gt;在解决方案上右键，然后选择“设置项目依赖”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-12-17-34.png&quot; alt=&quot;设置项目依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在弹出的项目依赖对话框中选择一个项目的依赖：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-12-18-39.png&quot; alt=&quot;选择项目依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，如果看看解决方案文件（.sln）则可以看到多出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectDependencies&lt;/code&gt; 区：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
	ProjectSection(ProjectDependencies) = postProject
		{98FF9756-B95A-4FDB-9858-5106F486FBF3} = {98FF9756-B95A-4FDB-9858-5106F486FBF3}
	EndProjectSection
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更多关于 sln 文件的理解，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-sln-file&quot;&gt;理解 Visual Studio 解决方案文件格式（.sln）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;其他方法&quot;&gt;其他方法&lt;/h2&gt;

&lt;p&gt;本文的方法已加入到此类型解法的方法列表中，详情请看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/affects-project-building-order.html&quot;&gt;三种方法设置 .NET/C# 项目的编译顺序，而不影响项目之间的引用 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 24 Jun 2020 01:04:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/setup-project-dependencies-in-the-solution-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/setup-project-dependencies-in-the-solution-file.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>通过 ReferenceOutputAssembly=False 在引用项目时，不额外引入依赖文件</title>
        <description>&lt;p&gt;正常当两个 .NET 项目有引用的时候，会将一个的输出拷贝到另一个的输出目录下。但有时我们只是希望通过引用建立一个依赖关系而已，最终两个项目的输出是独立的。&lt;/p&gt;

&lt;p&gt;通过本文的方法，你可以在 A 项目编译时，确保 B 项目已经编译，而无需引用 B。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;referenceoutputassemblyfalse&quot;&gt;ReferenceOutputAssembly=False&lt;/h2&gt;

&lt;p&gt;依然在项目中使用往常习惯的方法设置项目引用：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-12-04-50.png&quot; alt=&quot;设置项目引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，在项目引用设置完成之后，需要打开项目的项目文件（.csproj）给 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectReference&lt;/code&gt; 节点加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferenceOutputAssembly&lt;/code&gt; 的属性设置，将其值设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。这表示仅仅是项目引用，而不将项目的任何输出程序集作为此项目的依赖。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Analyzer\Walterlv.Demo.Analyzer.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Build\Walterlv.Demo.Build.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectReference&lt;/code&gt; 是 Sdk 风格的 csproj 文件中的项目引用。即便不是 Sdk 风格，也是一样的加这个属性。&lt;/p&gt;

&lt;p&gt;当然，你写多行也是可以的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Analyzer\Walterlv.Demo.Analyzer.csproj&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ReferenceOutputAssembly&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ReferenceOutputAssembly&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectReference&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.Build\Walterlv.Demo.Build.csproj&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ReferenceOutputAssembly&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ReferenceOutputAssembly&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectReference&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种做法有两个非常棒的用途：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;生成代码
    &lt;ul&gt;
      &lt;li&gt;依赖的项目（如上面的 Walterlv.Demo.Build）编译完成之后会生成一个可执行程序，它的作用是为我们当前的项目生成新的代码的。&lt;/li&gt;
      &lt;li&gt;于是我们仅仅需要在编译当前项目之前先把这个依赖项目编译好就行，并不需要生成运行时的依赖。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;NuGet 包中附带其他文件
    &lt;ul&gt;
      &lt;li&gt;如果要生成 NuGet 包，我们有时需要多个项目生成的文件来共同组成一个 NuGet 包，这个时候我们需要的仅仅是把其他项目生成的文件放到 NuGet 包中，而不是真的需要在 NuGet 包级别建立对此项目的依赖。&lt;/li&gt;
      &lt;li&gt;当使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferenceOutputAssembly&lt;/code&gt; 来引用项目，最终生成的 NuGet 包中就不会生成对这些项目的依赖。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;其他方法&quot;&gt;其他方法&lt;/h2&gt;

&lt;p&gt;本文的方法已加入到此类型解法的方法列表中，详情请看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/affects-project-building-order.html&quot;&gt;三种方法设置 .NET/C# 项目的编译顺序，而不影响项目之间的引用 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 24 Jun 2020 00:42:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/reference-a-project-without-referencing-output-assembly.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/reference-a-project-without-referencing-output-assembly.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome）</title>
        <description>&lt;p&gt;WPF 自定义窗口样式有多种方式，不过基本核心实现都是在修改 Win32 窗口样式。然而，Windows 上的应用就应该有 Windows 应用的样子嘛，在保证自定义的同时也能与其他窗口样式保持一致当然能最大程度保证 Windows 操作系统上的体验一致性。&lt;/p&gt;

&lt;p&gt;本文将使用 WindowChrome 来自定义窗口样式，使其既保留原生窗口样式和交互习惯，又能够具备一定的自定义空间。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-windows-原生窗口体验的应用&quot;&gt;使用 Windows 原生窗口体验的应用&lt;/h2&gt;

&lt;p&gt;在自定义窗口样式的同时保证一致的 Windows 窗口风格体验的优秀应用有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Windows 10 UWP 应用
    &lt;ul&gt;
      &lt;li&gt;当然少不了 UWP 应用，毕竟这就是 Windows 10 窗口体验的代表&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Google Chrome
    &lt;ul&gt;
      &lt;li&gt;如果我不提第三方应用，你们肯定会说微软都是自己拿内部 API，拿黑科技做的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 文件资源管理器
    &lt;ul&gt;
      &lt;li&gt;Windows 文件资源管理器也有一些自定义（例如在标题栏上放按钮，虽然实际做得很丑），不过整体来说还没 Chrome 做得精致呢&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-09-22-32.png&quot; alt=&quot;Chrome 普通窗口&quot; /&gt;&lt;br /&gt;
▲ Chrome 普通窗口&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-09-21-05.png&quot; alt=&quot;Chrome 最大化窗口&quot; /&gt;&lt;br /&gt;
▲ Chrome 最大化窗口&lt;/p&gt;

&lt;h2 id=&quot;为什么不做无边框窗口&quot;&gt;为什么不做无边框窗口？&lt;/h2&gt;

&lt;p&gt;WPF 自定义窗口可是非常容易的，完全自定义样式、异形都不在话下。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Whitman.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Whitman&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Whitman&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;WindowStyle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;None&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AllowsTransparency=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，这就不贴近原生窗口体验了，有这么多事情都不好模拟：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;最小化、最大化、关闭按钮
    &lt;ul&gt;
      &lt;li&gt;按钮要多大？位置在哪里？图标边距又是多少，颜色值又是什么？鼠标滑入划出的动画效果如何？&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;窗口标题栏交互
    &lt;ul&gt;
      &lt;li&gt;标题栏上有右键菜单，如果自己模拟，基本上这个就要自己重新实现了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;窗口的位置和尺寸
    &lt;ul&gt;
      &lt;li&gt;你需要自己实现一套窗口的拖拽调整位置功能，需要自己实现一套拖拽调整大小的功能。而自己实现的方式在触摸屏下还很容易出现失效的情况。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;窗口的阴影
    &lt;ul&gt;
      &lt;li&gt;要完全模拟 Windows 10 上的窗口阴影效果实在是一件头疼的事情，因为并不知道各种阴影参数是多少；就算模拟出来，性能也是个严重的问题。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;窗口的边框颜色
    &lt;ul&gt;
      &lt;li&gt;虽然窗口边框是被广为吐槽的一点，但为了保证一致的窗口体验，这也是需要模拟的；正常情况和失焦的情况颜色还不一样。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;第三方应用集成
    &lt;ul&gt;
      &lt;li&gt;第三方截图应用可以毫无障碍地捕捉到标准窗口的外框范围，但如果我们没有模拟好（而是拿一个 WPF 无边框窗口模拟），那么第三方截图应用就截不准（可能会超出窗口本来的大小）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;开始使用-windowchrome&quot;&gt;开始使用 WindowChrome&lt;/h2&gt;

&lt;p&gt;你也许需要先阅读 &lt;a href=&quot;https://www.cnblogs.com/dino623/p/uielements_of_window.html&quot;&gt;Window 的 UI 元素及行为 - dino.c&lt;/a&gt; 了解一些基本概念。&lt;/p&gt;

&lt;p&gt;理论上 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 的使用是非常简单的（呃……理论上）。你只需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Window /&amp;gt;&lt;/code&gt; 节点里写如下代码便能够完成客户区（Client Area）到非客户区（Non-client Area）的覆盖：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，默认的行为却并不那么像原生 Windows 10 窗口。事实上，这样的写法只是简单地把窗口的客户区覆盖到非客户区，原生窗口中的交互还在，但样式都已经被遮挡了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-13-45.png&quot; alt=&quot;样式已经被遮挡&quot; /&gt;&lt;br /&gt;
▲ 样式已经被遮挡&lt;/p&gt;

&lt;p&gt;不止是样式被遮挡，我们应该能注意相比于原生还有这些不同：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们的边框是白色的，原生的边框是系统主题色&lt;/li&gt;
  &lt;li&gt;鼠标划入我们窗口内才开始拖拽改变大小，但原生的在阴影区域就能开始调整大小了&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-21-11.png&quot; alt=&quot;拖拽的热区不同&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，为了能够观察到 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 各种属性设置的效果，我们为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 定义一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt;，里面就是空的，这样就没有什么内容能够遮挡我们设置的样式了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.Template&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.Template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-47-02.png&quot; alt=&quot;没有遮挡的窗口&quot; /&gt;&lt;br /&gt;
▲ 没有遮挡的窗口&lt;/p&gt;

&lt;p&gt;然而即便如此，我们也只解决了系统主题色边框的问题，没有解决调整窗口的拖拽热区问题。而且边框还如此之丑。&lt;/p&gt;

&lt;h3 id=&quot;glassframethickness&quot;&gt;GlassFrameThickness&lt;/h3&gt;

&lt;p&gt;在官方文档 &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.windows.shell.windowchrome.glassframecompletethickness(v=vs.110).aspx&quot;&gt;WindowChrome.GlassFrameCompleteThickness Property (System.Windows.Shell)&lt;/a&gt; 中有说，如果指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt; 值为 -1，那么可以做到整个窗口都遮挡，但实际上全遮挡的效果也是不对劲的，就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-45-32.png&quot; alt=&quot;GlassFrameThickness 为 -1&quot; /&gt;&lt;br /&gt;
▲ GlassFrameThickness 为 -1&lt;/p&gt;

&lt;p&gt;不止边框颜色不见了，连右上角的三个按钮的位置都跟原生不同，这个窗口的位置不贴边。&lt;/p&gt;

&lt;p&gt;显然，&lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt; 属性我们不能指定为 -1。&lt;/p&gt;

&lt;p&gt;也不能指定为 0，你可以试试，会发现连阴影都不见了，这更不是我们想要的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-54-15.png&quot; alt=&quot;GlassFrameThickness 为 0&quot; /&gt;&lt;br /&gt;
▲ GlassFrameThickness 为 0&lt;/p&gt;

&lt;p&gt;那我们指定为其他正数呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-14-59-42.png&quot; alt=&quot;指定为其他正数&quot; /&gt;&lt;br /&gt;
▲ 指定为其他正数&lt;/p&gt;

&lt;p&gt;显然，没有一个符合我们的要求。&lt;/p&gt;

&lt;h3 id=&quot;nonclientframeedges&quot;&gt;NonClientFrameEdges&lt;/h3&gt;

&lt;p&gt;但好在我们还有一个属性可以尝试 —— &lt;code class=&quot;highlighter-rouge&quot;&gt;NonClientFrameEdges&lt;/code&gt;。官方文档 &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.windows.shell.windowchrome.nonclientframeedges%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;WindowChrome.NonClientFrameEdges Property (System.Windows.Shell)&lt;/a&gt; 对此的解释是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Gets or sets a value that indicates which edges of the window frame are not owned by the client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即指定哪一边不属于客户区。&lt;/p&gt;

&lt;p&gt;考虑到我们前面的尝试中发现左、下、右的边框都是不符合要求的，所以我们现在将值设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Left,Bottom,Right&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NonClientFrameEdges=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left,Bottom,Right&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-05-26.png&quot; alt=&quot;比较接近的效果&quot; /&gt;&lt;br /&gt;
▲ 比较接近的效果&lt;/p&gt;

&lt;p&gt;这回我们终于看到了比较接近原生窗口的效果了，除了窗口的边框效果在激活和非激活状态下与原生窗口一致，连右上角三个按钮的位置也是贴近原生窗口的。甚至拖拽调整窗口大小时的光标热区也是类似的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-07-40.png&quot; alt=&quot;拖拽光标热区&quot; /&gt;&lt;br /&gt;
▲ 拖拽光标热区&lt;/p&gt;

&lt;p&gt;唯一不符合要求的是标题栏高度，这时我们可以继续设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt;，把顶部设置得更高一些。&lt;/p&gt;

&lt;p&gt;然而设置到多少呢？我测量了一下 Microsoft Store 应用的按钮高度，是 32。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-10-48.png&quot; alt=&quot;Microsoft Store 标题栏&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，这 32 包括了顶部 1 像素的边框吗？我使用放大镜查看，发现是包含的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-14-16.png&quot; alt=&quot;Microsoft Store 标题栏放大后&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt; 属性也是包含这个 1 像素边框的。所以含义一致，我们可以考虑直接将 32 设置到属性中：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 32 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NonClientFrameEdges=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left,Bottom,Right&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而实测结果是 —— 又被耍了，虽然标题栏有 32 的高度，但按钮只有 30 而已：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-18-16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而且在最大化窗口之后，按钮高度继续压缩。标题栏只剩下 24 的高度，按钮只剩下 22 的高度了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-19-32.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这显然也模拟得不像。于是，我们霸气一点，直接把顶部边距改得更大。为了凑个整，我写 64 好了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 64 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NonClientFrameEdges=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left,Bottom,Right&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;虽然正常状态下的按钮依然是 30 高度，但最大化时还是 30 高度这一点与原生 UWP 窗口和 Chrome 的行为是类似的。（UWP 窗口按钮 32 高度，最大化 32 高度；Google Chrome 窗口按钮 30 高度，最大化 27 高度。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-24-51.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以，截至这里，我们算是模拟得比较像了。&lt;/p&gt;

&lt;p&gt;其他的属性需要尝试吗？&lt;code class=&quot;highlighter-rouge&quot;&gt;CornerRadius&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeBorderThickness&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ResizeGripDirection&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;UseAeroCaptionButtons&lt;/code&gt; 在默认情况下的行为就已经够了；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsHitTestVisibleInChrome&lt;/code&gt; 是个与 WPF 相关的附加属性，与模拟窗口样式没有关系。所以基本模拟就靠前面的两个属性了。&lt;/p&gt;

&lt;h2 id=&quot;定制-window-的控件模板&quot;&gt;定制 Window 的控件模板&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 提供客户区内容覆盖到非客户区的能力，所以我们通过定制 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 能够在保证原生窗口体验的同时，尽可能定制我们的窗口样式。&lt;/p&gt;

&lt;p&gt;在按照以上的方式设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 之后，我们能够定制的客户区已经有下图所示的这么多了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-35-28.png&quot; alt=&quot;可定制的客户区&quot; /&gt;&lt;br /&gt;
▲ 可定制的客户区&lt;/p&gt;

&lt;p&gt;特别注意：&lt;strong&gt;可定制区域中顶部是包含那 1 像素的边距的，但其他三边不包含。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;下面的窗口是我在 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名）&lt;/a&gt; 中所述算法的一个应用，除了右上角的一个白色块，在保证接近原生窗口的情况下，定制了一些内容。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-44-14.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 一个试验品&lt;/p&gt;

&lt;p&gt;为了保证标题栏的标题文字也尽可能地接近原生窗口，我也通过测量得出了用于显示标题的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;TextBlock /&amp;gt;&lt;/code&gt; 的各种参数。整理之后，写成了下面的样式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-15-43-33.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.Template&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Padding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 30 0 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Top&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 -29 140 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                               &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Title}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate.Triggers&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Trigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowState&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Maximized&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Margin&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;6&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Trigger&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate.Triggers&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.Template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意，我写了一个触发器，当窗口最大化时根元素边距值设为 6。如果不设置，最大化时窗口边缘的像素将看不见。这是反复尝试的经验值，且在多种 DPI 下验证是依然有效的。实际上即便是最合适此时设置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SystemParameters.WindowResizeBorderThickness&lt;/code&gt; 属性依然无法让窗口最大化时边缘距离保持为 0。&lt;/p&gt;

&lt;h2 id=&quot;标题栏上的三大金刚&quot;&gt;标题栏上的三大金刚&lt;/h2&gt;

&lt;p&gt;我们发现，在以上所有方法尝试完成后，还剩下右上角的三颗按钮的背景色无法定制。如果依然采用非客户区控件覆盖的方法，这三个按钮就会被遮挡，只能自己区模拟了，那是不小的工作量。&lt;/p&gt;

&lt;p&gt;然而我们还发现，Google Chrome 是定制了这三个按钮的背景色的，正在研究它的做法。&lt;/p&gt;

&lt;p&gt;不过 Win32 原生的方法顶多只支持修改标题栏按钮的背景色，而不支持让标题栏按钮全透明。也就是说，Win32 原生方法也许能达到 Google Chrome 的效果，但不可能达到 UWP 中的效果。&lt;/p&gt;

&lt;p&gt;为了完全模拟 UWP，标题栏上的按钮只能自绘了。关于自绘标题栏按钮以模拟 UWP 原生按钮，可以阅读我的另一篇文章（代码太长，还是分开了好）：&lt;a href=&quot;/post/wpf-simulate-native-window-title-bar-buttons&quot;&gt;WPF 应用完全模拟 UWP 的标题栏按钮&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;原生-windows-窗口体验&quot;&gt;原生 Windows 窗口体验&lt;/h2&gt;

&lt;p&gt;UWP 应用对窗口样式的定制能力是非常小的，远远小于传统 Win32 应用。但因为其与系统原生集成，如果要求保证原生窗口体验，UWP 的定制能力又是各种方法里面最大的，而且 API 非常简单。&lt;/p&gt;

&lt;p&gt;如果你正在使用 UWP 开发应用，可参考林德熙的博客 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E6%A0%87%E9%A2%98%E6%A0%8F.html&quot;&gt;win10 uwp 标题栏&lt;/a&gt; 来定制标题栏。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmsetwindowattribute?wt.mc_id=MVP&quot;&gt;DwmSetWindowAttribute function - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.pinvoke.net/default.aspx/Enums/DwmSetWindowAttribute.html&quot;&gt;pinvoke.net: DwmSetWindowAttribute (Enums)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20120326-00/?p=8003&quot;&gt;Why does a maximized window have the wrong window rectangle? - The Old New Thing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 23 Jun 2020 07:16:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-simulate-native-window-style-using-window-chrome.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-simulate-native-window-style-using-window-chrome.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET 程序集/项目/包的版本号设置有最大范围，最大不能超过 65534</title>
        <description>&lt;p&gt;试过给 .NET Core 项目设置一个大于 65535 的版本号吗？可能没有，因为设置了会炸！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;最简问题项目&quot;&gt;最简问题项目&lt;/h2&gt;

&lt;p&gt;用最普通的项目模板创建一个 .NET 项目（要求是 SDK 风格的），于是，你会得到两个文件：项目文件 Walterlv.Demo.csproj 和代码文件 Class1.cs。&lt;/p&gt;

&lt;p&gt;Walterlv.Demo.csproj：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Class1.cs 应该不用贴出来了，因为没啥关系。&lt;/p&gt;

&lt;p&gt;现在，我们加个版本号：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Version&amp;gt;1.0.0.65535&amp;lt;/Version&amp;gt;
&lt;/span&gt;        &amp;lt;TargetFramework&amp;gt;netcoreapp3.1&amp;lt;/TargetFramework&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一编译就立刻编译错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-18-18-01-22.png&quot; alt=&quot;编译错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，只要把版本号改到 65534 或者更小的值就没有问题。&lt;/p&gt;

&lt;p&gt;因为我们可以知道，在 SDK 风格的项目当中，版本号的任何一位的范围只能是 0~65534。&lt;/p&gt;

&lt;h2 id=&quot;传统项目没问题&quot;&gt;传统项目没问题&lt;/h2&gt;

&lt;p&gt;你可能会说，创建了一个 .NET Framework 的项目，并没有出现问题。&lt;/p&gt;

&lt;p&gt;那是因为此问题的复现要求：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;必须是 SDK 风格的项目（.NET Core 默认的风格，也可用于 .NET Framework）；&lt;/li&gt;
  &lt;li&gt;必须是通过 .csproj 或者 .props / .targets 文件来指定的版本号。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这两个条件缺一不可。而通过模板创建的 .NET Framework 项目默认使用的是传统风格的 csproj 项目文件。&lt;/p&gt;

&lt;p&gt;如果是传统风格的项目，必须使用 AssemblyInfo.cs 来指定版本号；新的 SDK 风格的版本号也可以使用 AssemblyInfo.cs 来指定版本号。而这两种情况的版本号范围是整个 int 范围（0~2G）。&lt;/p&gt;

&lt;p&gt;附，在 SDK 风格项目中使用 AssemblyInfo.cs 来指定版本号前，你需要先用以下属性关闭默认自动生成 AssemblyInfo.cs 功能：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.1&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;GenerateAssemblyInfo&amp;gt;False&amp;lt;/GenerateAssemblyInfo&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;谁的限制&quot;&gt;谁的限制？&lt;/h2&gt;

&lt;p&gt;实际上，版本号限制是 Windows 系统带来的，Windows 系统限制到 65535 了。&lt;/p&gt;

&lt;p&gt;虽然你可以通过以上 AssemblyInfo 的方法绕过编译错误，但实际上生成的文件版本会溢出：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-19-00-04-19.png&quot; alt=&quot;溢出的版本号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;.NET 运行时是可以支持 int 范围的版本号的，无奈兼容 Windows 的部分却不行。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/archive/blogs/msbuild/why-are-build-numbers-limited-to-65535&quot;&gt;Why are build numbers limited to 65535? - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/37941296/6233938&quot;&gt;c# - The specified version string does not conform to the required format - major[.minor[.build[.revision]]] - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 18 Jun 2020 16:05:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-version-number-too-large.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-version-number-too-large.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Visual Studio 在编译 A 项目时，确保 B 项目已编译</title>
        <description>&lt;p&gt;如果考虑在你的某个项目中安插一个专门用来做编译的项目，这个项目要求最先编译，那么你会考虑用什么方法呢？&lt;/p&gt;

&lt;p&gt;本文讲述在编译 A 项目时，确保 B 项目已编译的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-msbuild-编译目标来编译&quot;&gt;使用 MSBuild 编译目标来编译&lt;/h2&gt;

&lt;p&gt;A 在编译的时候，需要确保 B 项目已经编译（因为可能用到 B 的输出）。&lt;/p&gt;

&lt;p&gt;然而 A 项目并不需要引用 B，因为仅仅是编译需要用到 B 而已，不需要在最终产品中带上 B。&lt;/p&gt;

&lt;p&gt;那么在 A 项目中，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 编译任务来编译 B：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BuildTheCompilerProject&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuild&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Projects=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Packages.Compiler\Walterlv.Packages.Compiler.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Properties=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Configuration=$(Configuration);Platform=$(Platform)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而 B 项目，则可能是框架完全不兼容的另一种项目：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;其他方法&quot;&gt;其他方法&lt;/h2&gt;

&lt;p&gt;本文的方法已加入到此类型解法的方法列表中，详情请看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/affects-project-building-order.html&quot;&gt;三种方法设置 .NET/C# 项目的编译顺序，而不影响项目之间的引用 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 18 Jun 2020 00:53:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-another-project-in-msbuild-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-another-project-in-msbuild-targets.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>理解 Visual Studio 解决方案文件格式（.sln）</title>
        <description>&lt;p&gt;一般情况下我们并不需要关心 Visual Studio 解决方案文件格式（.sln），因为 Visual Studio 对解决方案文件的自动修复能力是非常强的。但是如果遇到自动解冲突错误或者编译不通过了，那么此文件还是需要手工修改的。&lt;/p&gt;

&lt;p&gt;本文介绍 Visual Studio 解决方案（.sln）文件的格式。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;基本概念&quot;&gt;基本概念&lt;/h2&gt;

&lt;p&gt;Visual Studio 的解决方案文件由这三个部分组成：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;版本信息
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft Visual Studio Solution File, Format Version 12.00&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;# Visual Studio Version 16&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualStudioVersion = 16.0.28606.126&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MinimumVisualStudioVersion = 10.0.40219.1&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;项目信息
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;全局信息
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Global&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EndGlobal&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;虽然看起来是三个独立的部分，但其实除了版本号之外，项目信息和全局信息还是有挺多耦合部分的。&lt;/p&gt;

&lt;p&gt;比如我们来看一个 sln 文件的例子，是一个最简单的只有一个项目的 sln 文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-10-47-00.png&quot; alt=&quot;只有一个项目的 sln 文件&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {F2F1AD1B-207B-4731-ABEB-92882F89B155}
	EndGlobalSection
EndGlobal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面我们来一一说明。&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;h3 id=&quot;版本信息&quot;&gt;版本信息&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;记录文件的格式版本是 12.0。使用 Visual Studio 2019 编辑/创建。&lt;/p&gt;

&lt;p&gt;这里有一个小技巧，这里的 VisualStudioVersion 版本号设置为 15.0 会使得打开 sln 文件的时候默认使用 Visual Studio 2017，而设置为 16.0 会使得打开 sln 文件的时候默认使用 Visual Studio 2019。&lt;/p&gt;

&lt;h3 id=&quot;项目信息&quot;&gt;项目信息&lt;/h3&gt;

&lt;h4 id=&quot;project&quot;&gt;Project&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;项目信息至少由两行组成，第一行标记项目信息开始，而最后一行表示信息结束。&lt;/p&gt;

&lt;p&gt;其格式为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{项目类型}&quot;) = &quot;项目名称&quot;, &quot;项目路径&quot;, &quot;项目 Id&quot;
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以在我的另一篇博客中找到项目类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/a-list-of-project-type-guids&quot;&gt;解决方案文件 sln 中的项目类型 GUID&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是本文列举几个 .NET/C# 项目中的常见类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;9A19103F-16F7-4668-BE54-9A1E7A4F7556&lt;/code&gt; SDK 风格的 C# 项目文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FAE04EC0-301F-11D3-BF4B-00C04F79EFBC&lt;/code&gt; 传统风格的 C# 项目文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;2150E333-8FDC-42A3-9474-1A3956D46DE8&lt;/code&gt; 解决方案文件夹&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 SDK 风格的项目文件，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;项目名称和项目路径不必多说，都知道。对于文件夹而言，项目名称就是文件夹的名称，而项目路径也是文件夹的名称。&lt;/p&gt;

&lt;p&gt;项目 Id 是在解决方案创建项目的过程中生成的一个新的 GUID，每个项目都不一样。对于 SDK 风格的 C# 项目文件，csproj 中可以指定项目依赖，而如果没有直接的项目依赖，而只是解决方案编译级别的依赖，那么也可以靠 sln 文件中的项目 Id 来指定项目的依赖关系。另外，也通过项目 Id 来对项目做一些编译上的解决方案级别的配置。&lt;/p&gt;

&lt;h4 id=&quot;projectsection&quot;&gt;ProjectSection&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt; 的内部还可以放 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectSection&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;比如对于解决方案文件夹，可以包含解决方案文件：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{2150E333-8FDC-42A3-9474-1A3956D46DE8}&quot;) = &quot;Solution Items&quot;, &quot;Solution Items&quot;, &quot;{B002382D-4C9E-4F08-85E5-F12E2C061F5A}&quot;
	ProjectSection(SolutionItems) = preProject
		.gitattributes = .gitattributes
		.gitignore = .gitignore
		README.md = README.md
		build\Version.props = build\Version.props
	EndProjectSection
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个解决方案文件夹中包含了四个文件，其路径分别记录在了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectSection&lt;/code&gt; 节点里面。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectSection&lt;/code&gt; 还可以记录项目依赖关系（非项目之间的真实依赖，而是解决方案级别的编译依赖）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
	ProjectSection(ProjectDependencies) = postProject
		{98FF9756-B95A-4FDB-9858-5106F486FBF3} = {98FF9756-B95A-4FDB-9858-5106F486FBF3}
	EndProjectSection
EndProject
Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo2&quot;, &quot;Walterlv.Demo2\Walterlv.Demo2.csproj&quot;, &quot;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&quot;
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这一段节点里面，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 项目依赖于另外一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo2&lt;/code&gt; 项目。依赖是以 &lt;code class=&quot;highlighter-rouge&quot;&gt;项目 Id = 项目 Id&lt;/code&gt; 的方式写出来的；如果有多个依赖，那么就写多行。不用吐槽为什么一样还要写两遍，因为这是一个固定的格式，后面我们会介绍一些全局配置里面会有两个不一样的。&lt;/p&gt;

&lt;p&gt;关于设置项目依赖关系的方法，除了 sln 文件里面的设置之外，还有通过设置项目依赖属性的方式，详情可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/affects-project-building-order&quot;&gt;三种方法设置 .NET/C# 项目的编译顺序，而不影响项目之间的引用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;全局信息&quot;&gt;全局信息&lt;/h3&gt;

&lt;p&gt;一个全局信息的例子如下：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.Build.0 = Release|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {F2F1AD1B-207B-4731-ABEB-92882F89B155}
	EndGlobalSection
EndGlobal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这个全局信息的例子中，为解决方案指定了两个配置（Configuration），&lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt;，平台都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Any CPU&lt;/code&gt;。同时也为每个项目指定了单独的配置种类，可供选择，每一行都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;项目的配置 = 解决方案的配置&lt;/code&gt; 表示此项目的此种配置在解决方案的某个全局配置之下。&lt;/p&gt;

&lt;p&gt;如果我们将这两个项目放到文件夹中，那么我们可以额外看到一个新的全局配置 &lt;code class=&quot;highlighter-rouge&quot;&gt;NestedProjects&lt;/code&gt; 字面意思是说 &lt;code class=&quot;highlighter-rouge&quot;&gt;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&lt;/code&gt; 两个项目在 &lt;code class=&quot;highlighter-rouge&quot;&gt;{20B61509-640C-492B-8B33-FB472CCF1391}&lt;/code&gt; 项目中嵌套，实际意义代表 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo2&lt;/code&gt; 两个项目在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Folder&lt;/code&gt; 文件夹下。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GlobalSection(NestedProjects) = preSolution
    {DC0B1D44-5DF4-4590-BBFE-072183677A78} = {20B61509-640C-492B-8B33-FB472CCF1391}
    {98FF9756-B95A-4FDB-9858-5106F486FBF3} = {20B61509-640C-492B-8B33-FB472CCF1391}
EndGlobalSection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-24-15-34-02.png&quot; alt=&quot;在同一个文件夹下&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图解决方案下的整个解决方案全部内容如下：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
	ProjectSection(ProjectDependencies) = postProject
		{98FF9756-B95A-4FDB-9858-5106F486FBF3} = {98FF9756-B95A-4FDB-9858-5106F486FBF3}
	EndProjectSection
EndProject
Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo2&quot;, &quot;Walterlv.Demo2\Walterlv.Demo2.csproj&quot;, &quot;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&quot;
EndProject
Project(&quot;{2150E333-8FDC-42A3-9474-1A3956D46DE8}&quot;) = &quot;Folder&quot;, &quot;Folder&quot;, &quot;{20B61509-640C-492B-8B33-FB472CCF1391}&quot;
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{DC0B1D44-5DF4-4590-BBFE-072183677A78}.Release|Any CPU.Build.0 = Release|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(NestedProjects) = preSolution
		{DC0B1D44-5DF4-4590-BBFE-072183677A78} = {20B61509-640C-492B-8B33-FB472CCF1391}
		{98FF9756-B95A-4FDB-9858-5106F486FBF3} = {20B61509-640C-492B-8B33-FB472CCF1391}
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {F2F1AD1B-207B-4731-ABEB-92882F89B155}
	EndGlobalSection
EndGlobal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 18 Jun 2020 00:34:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/understand-the-sln-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/understand-the-sln-file.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>从 “x is null 和 x == null” 的区别看 C# 7 模式匹配中常量和 null 的匹配</title>
        <description>&lt;p&gt;尝试过写 &lt;code class=&quot;highlighter-rouge&quot;&gt;if (x is null)&lt;/code&gt;？它与 &lt;code class=&quot;highlighter-rouge&quot;&gt;if (x == null)&lt;/code&gt; 相比，孰优孰劣呢？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is constant&lt;/code&gt; 是 C# 7.0 中引入的模式匹配（Pattern Matching）中的一个小细节。阅读本文将了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is constant&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;x == constant&lt;/code&gt; 之间的差别，并给出一些代码编写建议。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;-c-7-的模式匹配&quot;&gt;🤓 C# 7 的模式匹配&lt;/h2&gt;

&lt;p&gt;说到 C# 中新增的模式匹配，想必大家一定不会忘了变量的匹配。以下例子来自于微软官方 C# 7.0 的介绍文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7?wt.mc_id=MVP&quot;&gt;What’s New in C# 7 - C# Guide - Microsoft Docs&lt;/a&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其实，官方文档中也顺带提及了常量的匹配：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PercentileDie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Multiplier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiceSum5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;unknown item type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，微软居然只在 &lt;code class=&quot;highlighter-rouge&quot;&gt;switch-case&lt;/code&gt; 里面说了常量的匹配，而且 &lt;code class=&quot;highlighter-rouge&quot;&gt;case 0&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;case null&lt;/code&gt; 这不本来就是我们以前熟悉的写法吗！（只不过以前只能判断一个类型的常量）&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;-x-is-null-vs-x--null&quot;&gt;🤔 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; Vs. &lt;code class=&quot;highlighter-rouge&quot;&gt;x == null&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;好了，回到正题。我们想说的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;x == null&lt;/code&gt;。为了得知它们的区别，我们写一段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestInWalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;反编译看看：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;TestInWalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxstack&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V_1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// [37 9 - 37 10]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          

    &lt;span class=&quot;c1&quot;&gt;// [38 13 - 38 31]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;       
    &lt;span class=&quot;n&quot;&gt;IL_0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;          
    &lt;span class=&quot;n&quot;&gt;IL_0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_0&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;IL_0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brfalse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;IL_000b&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// [39 13 - 39 14]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          

    &lt;span class=&quot;c1&quot;&gt;// [40 13 - 40 14]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_000a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          

    &lt;span class=&quot;c1&quot;&gt;// [41 13 - 41 31]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_000b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_000c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;       
    &lt;span class=&quot;n&quot;&gt;IL_000d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;          
    &lt;span class=&quot;n&quot;&gt;IL_000f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_1&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;IL_0010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0011&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brfalse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;IL_0015&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// [42 13 - 42 14]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0013&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          

    &lt;span class=&quot;c1&quot;&gt;// [43 13 - 43 14]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0014&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          

    &lt;span class=&quot;c1&quot;&gt;// [44 9 - 44 10]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;          

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method MainPage::Test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 对应的是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;       
&lt;span class=&quot;n&quot;&gt;IL_0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;          
&lt;span class=&quot;n&quot;&gt;IL_0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldarg.1&lt;/code&gt; 将第 1 号参数压到评估栈（为什么不是第 0 号？因为第 0 号是 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;）。然后将 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldnull&lt;/code&gt; 将 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 压到评估栈上。随后，&lt;code class=&quot;highlighter-rouge&quot;&gt;ceq&lt;/code&gt; 比较压入的两个值是否相等。&lt;em&gt;（注意是比较栈中的值哦，不会看引用的对象的！所以如果是引用类型，则比较的是引用本身哦，类似于指针！）&lt;/em&gt; &lt;strong&gt;此处划重点，因为考试要考！&lt;/strong&gt;咳咳……哦不，是后面要用到……&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x == null&lt;/code&gt; 对应的是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_000b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_000c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;       
&lt;span class=&quot;n&quot;&gt;IL_000d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;          
&lt;span class=&quot;n&quot;&gt;IL_000f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是发现两个完全一样！！！-_- 本文完，全剧终。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;然而，如果那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; 是一个重写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 的自定义类型就不一样了（感谢 &lt;a href=&quot;https://www.cnblogs.com/xuchonglei/&quot;&gt;TimAndy&lt;/a&gt; 提供的示例）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestInWalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;C&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;M1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;M2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 对应的是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0004&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x == null&lt;/code&gt; 对应的是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;op_Equality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，调用了重写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 运算符。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/xuchonglei/&quot;&gt;TimAndy&lt;/a&gt; 提供的示例详情在如下链接：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQGUCWBbADgGwgAUEB7AcwSm2QBoYRN8AfAAQCYBGAWACg/WAZgAEHYQGFhAbz7C5w4KVL5hAWU4AKSQA8AlMIC8APmHbhmZMIB2cfPgDcfAJCLla9ltP7jpwweu2DrLywXKYVjCmjrzyoiLiGuGRelIwABYWAHTaBtr2AL6hcaKcAGwKSiqkuBBUMKQIBv6epJy0EsKk7N4mrdl+nezZ0bFCJeWuVTV1DQCEzZKt7YvdPn1m84PDRWOkAG61CJgAJhAVbgCiAI5wUPjIGqTAAFYQAMaRXT3CGlq6X/0mlEdiJ9ocTmcksIAOIQGAACSgyDS4lIpw0320mVhCKRKLREAx0XyQA===&quot;&gt;https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQGUCWBbADgGwgAUEB7AcwSm2QBoYRN8AfAAQCYBGAWACg/WAZgAEHYQGFhAbz7C5w4KVL5hAWU4AKSQA8AlMIC8APmHbhmZMIB2cfPgDcfAJCLla9ltP7jpwweu2DrLywXKYVjCmjrzyoiLiGuGRelIwABYWAHTaBtr2AL6hcaKcAGwKSiqkuBBUMKQIBv6epJy0EsKk7N4mrdl+nezZ0bFCJeWuVTV1DQCEzZKt7YvdPn1m84PDRWOkAG61CJgAJhAVbgCiAI5wUPjIGqTAAFYQAMaRXT3CGlq6X/0mlEdiJ9ocTmcksIAOIQGAACSgyDS4lIpw0320mVhCKRKLREAx0XyQA===&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;-x-is-常量-vs-x--常量&quot;&gt;😏 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is 常量&lt;/code&gt; Vs. &lt;code class=&quot;highlighter-rouge&quot;&gt;x == 常量&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;现在我们把 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 换成其它常量：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestInWalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;😲呀……编译不通过！改改……&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestInWalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是再看看反编译出来的结果。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;value is 1&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;     
&lt;span class=&quot;n&quot;&gt;IL_0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mscorlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int32&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mscorlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_000d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;value == (object) 1&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IL_0013&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// 'value'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0014&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;     
&lt;span class=&quot;n&quot;&gt;IL_0015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mscorlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int32&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_001a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;          
&lt;span class=&quot;n&quot;&gt;IL_001c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// V_1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在已经不一样了，前者再比较时用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt;，调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool [mscorlib]System.Object::Equals(object, object)&lt;/code&gt; 方法；而后者依然用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ceq&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;区别已经很明显了，前者会根据具体类型具体判断相等，也就是说引用类型会调用引用类型自己的方法判断相等，值类型也会调用值类型的方法判断相等。而后者依然是比较评估栈中的两个值是否相等。关键是这两者均出现了装箱！也就是说——因为装箱的存在，对后者而言，&lt;code class=&quot;highlighter-rouge&quot;&gt;ceq&lt;/code&gt; 会压入 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，即永远返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，这就是 BUG 所在。这就是不一样的地方！&lt;/p&gt;

&lt;h2 id=&quot;如果重写了--或者-equals-呢&quot;&gt;🧐如果重写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt; 呢？&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.EqualsTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的执行结果是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;True
True
False
False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;他们的 IL 代码如下。可以看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt; 会调用重载的运算符和方法；而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt; 判断和前面是一样的，不受重载影响，可以和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Object&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt; 静态方法一样正常完成判空。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// foo == null&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dup&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EqualsTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;op_Equality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EqualsTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EqualsTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_000c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// foo.Equals(null)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0011&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dup&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0012&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0013&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callvirt&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0018&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// foo is null&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_001d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dup&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_001e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_001f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ceq&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Equals(foo, null)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0026&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldnull&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_0027&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IL_002c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.object.equals&quot;&gt;Object.Equals Method (System) - Microsoft Docs&lt;/a&gt; 了解到静态 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt; 方法的实现。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;回顾模式匹配中的常量匹配&quot;&gt;回顾模式匹配中的常量匹配&lt;/h2&gt;

&lt;p&gt;在 C# 7 的模式匹配中，&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 和常量其实都一样是常量，本来都是会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.Equals(object, object)&lt;/code&gt; 静态方法进行比较的；但 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 因为其特殊性，被编译器优化掉了，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;x == null&lt;/code&gt; 完全一样；&lt;code class=&quot;highlighter-rouge&quot;&gt;x is constant&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;x == constant&lt;/code&gt; 依然有区别。&lt;/p&gt;

&lt;p&gt;从反编译的 MSIL 代码中我们也可以得出一些代码编写上的建议。在比较常量的时候，如果可能，尽量使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt; 进行比较，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt;。好处多多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，写 &lt;code class=&quot;highlighter-rouge&quot;&gt;x is null&lt;/code&gt; 很符合英语的阅读习惯，代码阅读起来比较舒适。&lt;/li&gt;
  &lt;li&gt;如果是值常量，可以避免装箱带来的相等判断错误问题&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7?wt.mc_id=MVP&quot;&gt;What’s New in C# 7 - C# Guide - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/seteplia/2017/10/16/dissecting-the-pattern-matching-in-c-7/&quot;&gt;Dissecting the pattern matching in C# 7 – Dissecting the code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/40676426/what-is-the-difference-between-x-is-null-and-x-null&quot;&gt;c# - What is the difference between “x is null” and “x == null”? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.oschina.net/translate/whats-new-in-csharp-7-0&quot;&gt;C# 7.0 语言新特性 - 技术翻译 - 开源中国社区&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ceq%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;OpCodes.Ceq Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldarg_0%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;OpCodes.Ldarg_0 Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.stloc%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;OpCodes.Stloc Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldc_i4_1%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;OpCodes.Ldc_I4_1 Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 16 Jun 2020 02:39:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/is-null-vs-==-null.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/is-null-vs-==-null.html</guid>
        
        
        <category>csharp</category>
        
        <category>msil</category>
        
        <category>dotnet</category>
        
        <category>decompile</category>
        
      </item>
    
      <item>
        <title>如何根据一个绝对文件路径生成一个相对文件路径</title>
        <description>&lt;p&gt;日常的开发中，获取绝对文件路径才是主流吧！连 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetFullPath&lt;/code&gt; 这种生成绝对路径的方法都已经成为 .NET Standard 的一部分了。&lt;/p&gt;

&lt;p&gt;然而，生成相对路径依然有用——比如你的配置文件是相对于工作目录的，必须这个路径是输出给用户看的……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;.NET Core 2.0 开始，提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetRelativePath&lt;/code&gt; 这样的方法来获取两个路径之间的相对路径，但是 .NET Framework 却没有这样的方法可以用。&lt;/p&gt;

&lt;p&gt;那么，在旧版本的 .NET Core 或者 .NET Framework 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 没有生成相对路径的方法，还能怎么生成相对路径呢？&lt;em&gt;别跟我说自己去做字符串比较……&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Uri&lt;/code&gt; 提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeRelativeUri&lt;/code&gt; 方法，可以生成一个路径到另一个路径的相对路径。于是我们可以写出这样的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeRelativePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeRelativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnescapeDataString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\OpenSource\Demo&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\OpenSource\Demo\build\config.xml&lt;/code&gt;。结果，竟然得到的相对路径是：&lt;code class=&quot;highlighter-rouge&quot;&gt;Demo/build/config.xml&lt;/code&gt;。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&lt;/code&gt; 明明是两者共有的路径部分，却存在于相对路径中；&lt;/li&gt;
  &lt;li&gt;生成的路径使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;，而不是 Windows 系统使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是我们需要分别进行这两个处理。对于前者，我们必须让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Uri&lt;/code&gt; 意识到这是一个文件夹才能让最终生成的路径不带这个重复的部分；对于后者，我们需要进行路径连接符转换。于是最终的代码我整理成了如下方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeRelativePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 不是同一种路径，无法转换成相对路径。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\\&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果是文件系统，则视来源路径为文件夹。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeRelativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnescapeDataString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;relativePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AltDirectorySeparatorChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在重新传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\OpenSource\Demo&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\OpenSource\Demo\build\config.xml&lt;/code&gt;。结果，已经能够得到：&lt;code class=&quot;highlighter-rouge&quot;&gt;build\config.xml&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;关于 .NET Core 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetRelativePath&lt;/code&gt; 方法，可以参考：&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/Path.cs,4aa697c72b567ed8,references&quot;&gt;GetRelativePath&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/275689/6233938&quot;&gt;.net - How to get relative path from absolute path - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/Path.cs,4aa697c72b567ed8,references&quot;&gt;Path.GetRelativePath&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 15 Jun 2020 09:23:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/make-relative-file-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/make-relative-file-path.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑</title>
        <description>&lt;p&gt;一般来说，大家在需要监听全局消息的时候会考虑 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 这个 API。或者需要处理一些非自己编写的窗口的消息循环的时候，也会考虑使用它。&lt;/p&gt;

&lt;p&gt;如果要知道如何使用这个 API，你可以在网上搜到大量这样的文章/博客/教程/文档，然而大多不会提及使用此 API 时遇到的一些坑。阅读本文，你当然也可以知道应该如何使用这个 API，但同时也能了解如何正确使用以避免一些奇怪的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;基本使用&quot;&gt;基本使用&lt;/h2&gt;

&lt;p&gt;如果你在阅读本文的时候遇到了一些问题，可考虑去 GitHub 上克隆我的源码，跑一跑试试。在这里：&lt;a href=&quot;https://github.com/walterlv/Walterlv.Demo.SetWindowsHookEx&quot;&gt;walterlv/Walterlv.Demo.SetWindowsHookEx&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;简单一点，先贴出一部分可以工作起来的代码，你直接可以放到你的项目当中运行测试：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HookProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mouseHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hMouseHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_mouseHook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnMouseHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetModuleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 你可能会在网上搜索到下面注释掉的这种代码，但实际上已经过时了。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//   下面代码在 .NET Core 3.x 以上可正常工作，在 .NET Framework 4.0 以下可正常工作。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//   如果不满足此条件，你也可能可以正常工作，详情请阅读本文后续内容。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// var hModule = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_hMouseHook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowsHookEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;HookType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WH_MOUSE_LL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_mouseHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_hMouseHook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLastWin32Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Win32Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnMouseHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在这里，你可以处理全局鼠标消息。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallNextHookEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本文讨论使用 .NET/C# 来完成 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 的调用，所以自然少不了 P/Invoke（平台调用）。因此你必须将以下代码也添加到你的代码仓库中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetModuleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpModuleName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowThreadProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpdwProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowsHookEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HookType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hookType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HookProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpfn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hMod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallNextHookEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hhk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HookProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HookType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_JOURNALRECORD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_JOURNALPLAYBACK&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_KEYBOARD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_GETMESSAGE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_CALLWNDPROC&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_CBT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_SYSMSGFILTER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_MOUSE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_HARDWARE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_DEBUG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_SHELL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_FOREGROUNDIDLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_CALLWNDPROCRET&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_KEYBOARD_LL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WH_MOUSE_LL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;setwindowshookex&quot;&gt;SetWindowsHookEx&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 的签名如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;HHOOK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowsHookExA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;idHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;HOOKPROC&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;lpfn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DWORD&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;dwThreadId&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;当方法执行成功时，返回值是钩子处理函数的句柄，用于在钩子的消息处理中调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CallNextHookEx&lt;/code&gt; 方法。当方法执行失败时，这里返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;idHood 参数表示需要处理的消息类型（我们前面定义成了枚举类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;HookType&lt;/code&gt;）&lt;/li&gt;
  &lt;li&gt;lpfn 是自己定义的钩子的消息处理方法（对应我们前面定义的委托）&lt;/li&gt;
  &lt;li&gt;hmod 是模块的句柄，在本机代码中，对应 dll 的句柄（可在 dll 的入口函数中获取）；而我们是托管代码&lt;/li&gt;
  &lt;li&gt;dwThreadId 是线程 Id，传入 0 则为全局所有线程，否则传入特定的线程 Id&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;需要注意的坑&quot;&gt;需要注意的坑&lt;/h2&gt;

&lt;h3 id=&quot;模块句柄传什么&quot;&gt;模块句柄传什么？&lt;/h3&gt;

&lt;p&gt;本文一开始被注释掉的代码中，我使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal&lt;/code&gt; 直接从托管程序集中获取了模块句柄。&lt;/p&gt;

&lt;p&gt;这里需要说明，托管程序集不能注入到其他进程，因此也不可以挂接钩子。但有例外，&lt;code class=&quot;highlighter-rouge&quot;&gt;WH_KEYBOARD_LL&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_MOUSE_LL&lt;/code&gt; 这两个是不需要注入 dll 的，因此可以挂接钩子。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_KEYBOARD_LL&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_MOUSE_LL&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 方法里面根本没有使用这个模块做什么真正的事情，它只是验证一下一个模块而已。只要存在于你的进程中。&lt;/p&gt;

&lt;p&gt;所以，传入其他的模块都是可以的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;传入口模块也是可以的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHINSTANCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetModules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetModuleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这也是一开始我在 P/Invoke 的方法里面预留了 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadLibrary&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetModuleHandle&lt;/code&gt; 方法的原因。&lt;/p&gt;

&lt;p&gt;通过调试也能发现这两个的入口模块是相同的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-18-10-55-00.png&quot; alt=&quot;入口模块句柄&quot; /&gt;&lt;/p&gt;

&lt;p&gt;至于为什么可以用 user32.dll。嗯，反正我们创建窗口监听消息都已经大量调用 user32.dll 的 API 了，这 dll 肯定已经加入到我们的进程中了，所以我们把这个传入到参数中是可以通过验证的。&lt;/p&gt;

&lt;h3 id=&quot;错误-126找不到指定的模块&quot;&gt;错误 126：找不到指定的模块。&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;The specified module could not be found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果你只是拿代码做做 demo 可能一切顺利，但放到实际项目里面就挂得一塌糊涂：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-10-15-46-43.png&quot; alt=&quot;找不到指定的模块&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这也是我在一开始的 P/Invoke 里面加上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetLassError&lt;/code&gt; 的重要原因，因为这 API 容易挂。&lt;/p&gt;

&lt;p&gt;检查的错误码是 126（0x0000007E）。&lt;/p&gt;

&lt;p&gt;然而我的 dll 是存在的呀！&lt;/p&gt;

&lt;p&gt;让我们再来看我一开始预留的注释：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 下面代码在 .NET Core 3.x 以上可正常工作，在 .NET Framework 4.0 以下可正常工作。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 如果不满足此条件，你也可能可以正常工作，详情请阅读本文后续内容。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hModule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHINSTANCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetModules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是的，你遇到这样的异常，多半意味着你落入 .NET Framework 4.x 版本的运行时了。&lt;/p&gt;

&lt;p&gt;.NET Framework 4.0 相比于之前的 CLR 发生了很大的更改，不再假装 JIT 代码存在一非托管模块中，因此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.GetHINSTANCE&lt;/code&gt; 将不再起作用。&lt;/p&gt;

&lt;p&gt;对于低级钩子来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 需要一个有效的模块句柄进行检查，但实际上此 API 执行时根本没有使用这个模块。所以更推荐使用前一小节中提供的 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadLibrary&lt;/code&gt; 函数来获取模块句柄，而不是获取当前托管模块的句柄。&lt;/p&gt;

&lt;p&gt;解决方法，两/三个：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;方法一：使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadLibrary(&quot;user32.dll&quot;)&lt;/code&gt; 获取模块句柄代替 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.GetHINSTANCE&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;方法二：将获取句柄的模块改为入口程序集（exe），即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly()&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;方法三：升级成纯 .NET Core 程序&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;错误-1428没有模块句柄无法设置非本机的挂接&quot;&gt;错误 1428：没有模块句柄无法设置非本机的挂接。&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Cannot set nonlocal hook without a module handle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;对于前面说的 126 错误，你可能从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetExecutingAssembly&lt;/code&gt; 改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly()&lt;/code&gt; 之后会出现此异常。&lt;/p&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadLibrary(&quot;user32.dll&quot;)&lt;/code&gt; 获取模块句柄代替 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.GetHINSTANCE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;错误-1429此挂接程序只可整体设置&quot;&gt;错误 1429：此挂接程序只可整体设置。&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;This hook procedure can only be set globally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;估计找到这里的方式可能是搜索，因为这段中文读起来真的是太晦涩了。不过我把英文贴到上一行了，相信你差不多就知道是怎么回事了。&lt;/p&gt;

&lt;p&gt;因为你给 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 方法中传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HookType&lt;/code&gt; 参数指定了低级类型（Low Level，&lt;code class=&quot;highlighter-rouge&quot;&gt;HookType&lt;/code&gt; 枚举后面带了 LL 后缀的），这时只能全局设置钩子。意味着你的第四个参数必须传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;如何只处理特定窗口的消息&quot;&gt;如何只处理特定窗口的消息？&lt;/h3&gt;

&lt;p&gt;消息循环属于“线程”，而不是属于某个窗口或者进程。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateWindowEx&lt;/code&gt; 创建窗口时传入的消息处理函数会仅处理特定窗口的消息，然而当通过钩子的方式来处理消息的话，无法精确定位到某个特定的窗口，只能针对消息循环所在的线程。因此，要处理特定窗口的消息，只能先拿到此窗口所在的线程。&lt;/p&gt;

&lt;p&gt;前面的 P/Invoke 中我也预留了获取窗口所在线程的方法。因此，可以直接使用以下调用来获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;hWnd&lt;/code&gt; 句柄窗口所在的线程。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowThreadProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本来在 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 最后一个参数传入 0 表示全局钩子的，那么现在传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;threadId&lt;/code&gt; 即仅监听此线程的消息。&lt;/p&gt;

&lt;p&gt;另外，如果只是打算处理单个窗口的消息，而不是这个线程里的所有消息，那么建议使用子类化的方式来实现。详情可阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/hook-a-window-by-sub-classing-it.html&quot;&gt;通过子类化窗口（SubClass）来为现有的某个窗口添加新的窗口处理程序（或者叫钩子，Hook） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;为什么会导致其他进程闪退&quot;&gt;为什么会导致其他进程闪退？&lt;/h3&gt;

&lt;p&gt;你可能会发现，明明按照本文所述的方法挂接了钩子，但一运行起来后，其他程序（被挂接的程序）出现了闪退现象。&lt;/p&gt;

&lt;p&gt;接下来说明：&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;HookType&lt;/code&gt; 的所有种类中，只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_MOUSE_LL&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_KEYBOARD_LL&lt;/code&gt; 是不需要注入到目标进程的，其他都必须将 dll 注入到目标进程才可以完成挂接。然而 .NET 程序集无法被注入到其他进程；随便用一个其他 dll 时，里面没有被挂接的函数地址，在注入后就会导致目标进程崩溃。&lt;/p&gt;

&lt;p&gt;所以：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果需要挂接的进程就在本进程内（最后参数指定的线程是本进程内的线程），那么所有种类都可以挂接；&lt;/li&gt;
  &lt;li&gt;如果需要全局挂接，或者要挂接别的进程，那么 .NET 程序只能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_MOUSE_LL&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;WH_KEYBOARD_LL&lt;/code&gt; 两种挂接类型；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果就是要挂接其他类型的钩子怎么办？办法总还是有的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;可以考虑做非托管 dll，专门用来挂接；&lt;/li&gt;
  &lt;li&gt;可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWinEventHook&lt;/code&gt;，这个是不用注入到目标进程的；&lt;/li&gt;
  &lt;li&gt;可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Automation&lt;/code&gt; 抓取一部分有限的信息。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;其他问题&quot;&gt;其他问题&lt;/h2&gt;

&lt;p&gt;如果你在各种折腾之后还是有问题，可考虑去 GitHub 上克隆我的源码，跑一跑试试。在这里：&lt;a href=&quot;https://github.com/walterlv/Walterlv.Demo.SetWindowsHookEx&quot;&gt;walterlv/Walterlv.Demo.SetWindowsHookEx&lt;/a&gt;。或者通过本文后面附带的联系方式与我联系。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa&quot;&gt;SetWindowsHookExA function (winuser.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C&quot;&gt;Processing Global Mouse and Keyboard Hooks in C# - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17897646/setwindowshookex-fails-with-error-126&quot;&gt;c# - SetWindowsHookEx fails with error 126 - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9971175/how-to-pass-window-handle-to-wndproc&quot;&gt;winapi - How to pass window handle to wndproc? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/6872044/example-of-hooking-a-window&quot;&gt;.net - Example of hooking a window? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/18499468/6233938&quot;&gt;.net - SetWindowHookEx fails at runtime in C# application - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/22249063/6233938&quot;&gt;winapi - Is there a way to know when another hwnd has closed? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 13 Jun 2020 09:39:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-global-windows-hook-in-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-global-windows-hook-in-dotnet.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF / Windows Forms 检测窗口在哪个屏幕</title>
        <description>&lt;p&gt;使用 Windows Forms 自带的 System.Windows.Forms.Screen 类可以从一个窗口句柄获取到对应的屏幕。随后可以使用此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Screen&lt;/code&gt; 类获取各种屏幕信息。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;systemwindowsformsscreen&quot;&gt;System.Windows.Forms.Screen&lt;/h2&gt;

&lt;p&gt;通过句柄获取屏幕类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Forms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我做了一个 DEMO 程序，画出了窗口的位置和大小，以及当前窗口所在的屏幕的位置和大小。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-11-move-between-screen.gif&quot; alt=&quot;屏幕间移动的窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;附代码&quot;&gt;附代码&lt;/h2&gt;

&lt;p&gt;MainWindow.xaml&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Issues.Dpi.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Issues.Dpi&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎阅读吕毅的博客 blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootPanel&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SnapsToDevicePixels=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ScaleTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UIElement.RenderTransform&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScreenBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#E4E4E6&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScreenTranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScreenTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#949499&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowTranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 0 -1000 -1000&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MainWindow.xaml.cs&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media.Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Navigation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Issues.Dpi&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LocationChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MainWindow_LocationChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SizeChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MainWindow_SizeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow_LocationChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;UpdateScreenInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow_SizeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SizeChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;UpdateScreenInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateScreenInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Forms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ScreenTranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ScreenTranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ScreenBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ScreenBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ScreenTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$@&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WindowTranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WindowTranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WindowBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WindowBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WindowTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$@&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 11 Jun 2020 10:39:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detect-screen-that-contains-the-wpf-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detect-screen-that-contains-the-wpf-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>准确判断一个 WPF 控件 / UI 元素当前是否显示在屏幕内</title>
        <description>&lt;p&gt;你的 WPF 窗口是可以拖到屏幕外面去的，所以拉几个元素到屏幕外很正常。你的屏幕可能有多个。你的多个屏幕可能有不同的 DPI。你检测的元素可能带有旋转。&lt;/p&gt;

&lt;p&gt;各种各样奇怪的因素可能影响你检查此元素是否在屏幕内，本文包你一次性解决，绝对准确判断。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文将说三种不同的判定方法，分偷懒版、日常版和苛刻版：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你只是写个 demo 啥的，用偷懒版就够了，代码少性能高。&lt;/li&gt;
  &lt;li&gt;如果你在项目/产品中使用，使用日常版就好。&lt;/li&gt;
  &lt;li&gt;如果你的用户群体天天喷你 bug 多，那么用苛刻版更好。&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;偷懒版&quot;&gt;偷懒版&lt;/h2&gt;

&lt;p&gt;如果你只想写个 demo，那么此代码足以。&lt;/p&gt;

&lt;p&gt;判断 UI 元素的位置，其右侧是否在屏幕最左侧，其底部是否在屏幕最上面；或者其左侧是否在屏幕最右侧，其顶部是否在屏幕最下面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-11-11-56-53.png&quot; alt=&quot;偷懒版&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsOutsideOfScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenLeft&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenTop&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenLeft&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenWidth&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenTop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;日常版推荐&quot;&gt;日常版（推荐）&lt;/h2&gt;

&lt;p&gt;如果你检测的元素自带了旋转，那么以上方法就不能准确判断了。&lt;/p&gt;

&lt;p&gt;现在，我们需要检查这个元素的整个边界区域，即便是旋转后。于是，现在，我们要判断元素边界点所在的矩形区域了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-11-12-00-25.png&quot; alt=&quot;日常版&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 判断一个可视化对象是否在屏幕外面无法被看见。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;target&quot;&amp;gt;要判断的可视化元素。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果元素在屏幕外面，则返回 true；如果元素在屏幕里或者部分在屏幕里面，则返回 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsOutsideOfScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetScreenPixelBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intersect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;intersect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Intersect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intersect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 此 Visual 未连接到 PresentationSource。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetScreenPixelBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualScreenHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;苛刻版&quot;&gt;苛刻版&lt;/h2&gt;

&lt;p&gt;现在，更复杂的场景来了。&lt;/p&gt;

&lt;p&gt;如果用户有多台显示器，而且大小还不一样，那么依前面的判定方法，下图中 C 控件虽然人眼看在屏幕外，但计算所得是在屏幕内。&lt;/p&gt;

&lt;p&gt;更复杂的，是多台显示器还不同 DPI 时，等效屏幕尺寸的计算更加复杂。更恐怖的是，WPF 程序声明支持的 DPI 级别不同，计算也会有一些差别。想要写一种支持所有支持级别的代码更加复杂。但本文可以。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-11-14-45-08.png&quot; alt=&quot;苛刻版&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 判断一个可视化对象是否在屏幕外面无法被看见。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;target&quot;&amp;gt;要判断的可视化元素。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果元素在屏幕外面，则返回 true；如果元素在屏幕里或者部分在屏幕里面，则返回 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsOutsideOfScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screens&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Forms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AllScreens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IntersectsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelBoundsToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在下面这段代码中，即便是 WPF 项目，我们也需要引用 Windows Forms，用于获取屏幕相关的信息。&lt;/p&gt;

&lt;p&gt;如果是 SDK 风格的项目，则在 csproj 中添加如下代码：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;

    &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;WinExe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net5.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;UseWindowsForms&amp;gt;true&amp;lt;/UseWindowsForms&amp;gt;
&lt;/span&gt;    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果是传统风格的项目，则直接添加 System.Windows.Forms 程序集的引用就好。&lt;/p&gt;

&lt;p&gt;因为 WPF 的坐标单位是“设备无关单位”（我更倾向于叫有效像素，见 &lt;a href=&quot;https://blog.walterlv.com/post/introduce-uwp-effective-pixels-into-wpf.html&quot;&gt;有效像素（Effective Pixels）&lt;/a&gt;），所以在系统对窗口有缩放行为的时候，多屏不同 DPI 的计算相当复杂，所以这里我们使用纯 Win32 / Windows Forms 方法在来计算屏幕与 UI 元素之间的交叉情况，并且避免在任何时候同时将多个屏幕的坐标进行加减乘除（避免单位不一致的问题）。所以这段代码对任何 WPF 的 DPI 配置都是有效且准确的。&lt;/p&gt;

&lt;p&gt;关于 DPI 感知设置的问题，可阅读我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/windows-high-dpi-development.html&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/windows-high-dpi-development-for-wpf.html&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/view-process-info-using-task-manager.html&quot;&gt;Windows 系统上使用任务管理器查看进程的各项属性（命令行、DPI、管理员权限等） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此代码的唯一的缺点是，在 WPF 项目里面要求引用 Windows Forms。&lt;/p&gt;

&lt;h2 id=&quot;功能比较&quot;&gt;功能比较&lt;/h2&gt;

&lt;p&gt;不知道用哪个？看下表吧！&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;代码版本&lt;/th&gt;
      &lt;th&gt;偷懒版&lt;/th&gt;
      &lt;th&gt;日常版&lt;/th&gt;
      &lt;th&gt;苛刻版&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基础判断屏幕内外&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;高分屏（非 96 DPI）&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;整齐排列的多屏&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;元素带有旋转&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;多屏尺寸不统一&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;多屏有不同 DPI（WPF 感知系统 DPI）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;多屏有不同 DPI（WPF 感知屏幕 DPI）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;多屏有不同 DPI（WPF 感知屏幕 DPI V2）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;纯 WPF 代码（无需引用 Windows Forms）&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;元素形状不规则&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;性能&lt;/td&gt;
      &lt;td&gt;好&lt;/td&gt;
      &lt;td&gt;较好&lt;/td&gt;
      &lt;td&gt;一般&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Thu, 11 Jun 2020 09:40:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detect-whether-a-wpf-visual-is-inside-screen.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detect-whether-a-wpf-visual-is-inside-screen.html</guid>
        
        
        <category>wpf</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Compare four different file (folder) links on Windows (NTFS hard links, junction points, symbolic links, and well-known shortcuts)</title>
        <description>&lt;p&gt;It is well-known that &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; is a command to create a variety of links on NTFS disk. But if you don’t know much about it or even never hear of it, it doesn’t matter because you know shortcuts at least. This post help you to lean more about &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; and know the differences among the difference command options.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/ntfs-link-comparisons.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/ntfs-link-comparisons-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;different-ways-of-linking&quot;&gt;Different ways of linking&lt;/h2&gt;

&lt;p&gt;Windows Vista announced the NTFS symbolic links, Windows 2000 began to have NTFS Reparse Point, and earlier Windows 95 introduced the shortcut. Backing to Windows 3.5 There are hard links. All of them provide you the power to access a same folder or file in difference paths.&lt;/p&gt;

&lt;h3 id=&quot;mklink&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Using &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; command, you can create a “hard link”, “junction points” and “symbolic link”.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Creates&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;symbolic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MKLINK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Creates&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;symbolic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;symbolic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;link.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Creates&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;instead&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;symbolic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Creates&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Junction.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Specifies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;symbolic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;name.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Specifies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;e.g:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;APPDATA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\walterlv\packages\1.0.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is to create a junction point linking to &lt;code class=&quot;highlighter-rouge&quot;&gt;%APPDATA%\walterlv\packages\1.0.0&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;shortcuts&quot;&gt;Shortcuts&lt;/h3&gt;

&lt;p&gt;Shortcuts is a Windows feature which is different to those introduced by the NTFS.&lt;/p&gt;

&lt;p&gt;Shortcut is a file with a &lt;code class=&quot;highlighter-rouge&quot;&gt;lnk&lt;/code&gt; file extension. This file contains the info indicates how to open the linking file or directory. Maybe most applications use this &lt;code class=&quot;highlighter-rouge&quot;&gt;lnk&lt;/code&gt; file to execute their programs.&lt;/p&gt;

&lt;h3 id=&quot;others&quot;&gt;Others&lt;/h3&gt;

&lt;p&gt;Reparse Point has been in the Windows operating system since NTFS v3.0 (introduced with Windows 2000). In addition to our previously mentioned three types of reparse points made by &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt;, there are other types:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Volume Mount Ppoints&lt;/li&gt;
  &lt;li&gt;Distributed Link Tracking（DLT）&lt;/li&gt;
  &lt;li&gt;Data Deduplication&lt;/li&gt;
  &lt;li&gt;Hierarchical Storage Management（HSM）&lt;/li&gt;
  &lt;li&gt;Native Structured Storage（NSS）&lt;/li&gt;
  &lt;li&gt;Unix Doman Socket（socket）&lt;/li&gt;
  &lt;li&gt;System Compression&lt;/li&gt;
  &lt;li&gt;OneDrive&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comparison&quot;&gt;Comparison&lt;/h2&gt;

&lt;p&gt;Reading those words above, you may know the usage of &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; but don’t know the difference between them. Then I’ve post them below:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Hard Link&lt;/th&gt;
      &lt;th&gt;Junction Point&lt;/th&gt;
      &lt;th&gt;Symbolic Link&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Command&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /H Link Target&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /J Link Target&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /D Link Target&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Description&lt;/td&gt;
      &lt;td&gt;Create an alias for a file so that different paths correspond to the data of the same file.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Linking to files&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Linking to directories&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Needs to run as Administrator&lt;/td&gt;
      &lt;td&gt;Yes&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;Mostly Yes &lt;code class=&quot;highlighter-rouge&quot;&gt;[Tip1]&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Supports linking across volumes&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️(Local Machine only)&lt;/td&gt;
      &lt;td&gt;✔️(including remote path such as SMB)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Introduced since&lt;/td&gt;
      &lt;td&gt;Supports since Windows NT 3.1&lt;br /&gt;API supports since Windows 2000 by &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateHardLink()&lt;/code&gt;&lt;br /&gt;Supports since Windows NT 6.0 by command &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /H&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Windows 2000+&lt;/td&gt;
      &lt;td&gt;Windows Vista+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Supports targets which is not exist&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Link to relative directory&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌(You can create one with relative path but it will change to absolute path automatically.)&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How to remove&lt;/td&gt;
      &lt;td&gt;del&lt;/td&gt;
      &lt;td&gt;rd&lt;/td&gt;
      &lt;td&gt;rd / del&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;When the reparse point is removed&lt;/td&gt;
      &lt;td&gt;Only after all hard links to the original file and the original file have been deleted will the file data be deleted.&lt;/td&gt;
      &lt;td&gt;The original folder is not affected after Windows Vista but is will be deleted in Windows 2000 / XP / 2003.&lt;/td&gt;
      &lt;td&gt;The original file/folder is not affected.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;When the original file/folder is removed&lt;/td&gt;
      &lt;td&gt;The hard link can still access the data of the file normally.&lt;/td&gt;
      &lt;td&gt;Directory connection failed, pointing to a directory that does not exist.&lt;/td&gt;
      &lt;td&gt;The symbolic link is invalid and points to a directory that does not exist.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[Tip1]&lt;/code&gt;: A post of Microsoft says, Starting with Windows 10 Insiders build 14972, symlinks can be created without needing to elevate the console as administrator. This will allow developers, tools and projects, that previously struggled to work effectively on Windows due to symlink issues, to behave just as efficiently and reliably as they do on Linux or OSX. (Below picture shows how to open the developer’s mode.)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-09-44-50.png&quot; alt=&quot;Turn on the developer mode&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;extras&quot;&gt;Extras&lt;/h2&gt;

&lt;p&gt;If you create shortcuts in your start menu, and the shortcuts are linking to files that are in junction points. All the shortcuts will disappear after a Windows 10 updates. I’m reporting this bug to Microsoft, but before Microsoft resolve this bug we have to work around to avoid this bug. See the bug in scoop below:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/lukesampson/scoop/issues/3941&quot;&gt;After the windows 10 updates, all shortcuts of scoop will disappear. · Issue #3941 · lukesampson/scoop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/NTFS_reparse_point&quot;&gt;NTFS reparse point - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9042542/what-is-the-difference-between-ntfs-junction-points-and-symbolic-links&quot;&gt;windows - What is the difference between NTFS Junction Points and Symbolic Links? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hard_link&quot;&gt;Hard link - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links&quot;&gt;Create symbolic links (Windows 10) - Windows security - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/&quot;&gt;Symlinks in Windows 10! - Windows Developer Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 11 Jun 2020 01:28:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/ntfs-link-comparisons-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/ntfs-link-comparisons-en.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>比较 Windows 上四种不同的文件（夹）链接方式（NTFS 的硬链接、目录联接、符号链接，和大家熟知的快捷方式）</title>
        <description>&lt;p&gt;可能有很多小伙伴已经知道通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 命令来创建 NTFS 磁盘上的各种链接；当然，就算不知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 的链接，快捷方式应该每个人都知道吧。&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 的选项有很多种，但你可能在其他文章中难以找到对这些不同选项的不同效果和使用限制的准确和统一描述。本文将介绍 Windows 系统中所有的链接方式，它们的优缺点、使用条件和坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/ntfs-link-comparisons.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/ntfs-link-comparisons-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;不同的链接方式&quot;&gt;不同的链接方式&lt;/h2&gt;

&lt;p&gt;Windows Vista 开始带来了 NTFS 符号链接（Symbolic Link），Windows 2000 开始就有了 NTFS 重解析点（Reparse Point），更早的 Windows 95 就有了快捷方式（Shortcut），再往前到 Windows 3.5 还有硬链接（Hard Link），他们都能实现给你不同的路径访问同一个文件或文件夹的功能。&lt;/p&gt;

&lt;h3 id=&quot;mklink&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 命令，你可以创建“硬链接（Hard Link）”、“目录联接（Junction Point）”和“符号链接（Symbolic Link）”。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MKLINK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录符号链接。默认为文件&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建硬链接而非符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录联接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新的符号链接名称。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新链接引用的路径&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;相对或绝对&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;APPDATA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\walterlv\packages\1.0.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;即在当前目录创建了一个指向 &lt;code class=&quot;highlighter-rouge&quot;&gt;%APPDATA%\walterlv\packages\1.0.0&lt;/code&gt; 的目录联接。&lt;/p&gt;

&lt;p&gt;因为创建目录联接不需要管理员权限，所以特别适合给桌面应用程序用来按版本管理某些包/工具集。有关使用 .NET/C# 来创建目录联接的方法，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/mklink-junction-in-dotnet.html&quot;&gt;.NET 实现 NTFS 文件系统的硬链接 mklink /J（Junction） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;快捷方式&quot;&gt;快捷方式&lt;/h3&gt;

&lt;p&gt;快捷方式是一个单纯 Windows 操作系统用户层面的功能，与 NTFS 文件系统没有什么关系。不过其也能实现链接到另一个文件的功能。使用快捷方式的程序太多了，几乎每个安装包都会考虑往桌面或开始菜单扔几个快捷方式。&lt;/p&gt;

&lt;p&gt;快捷方式的本质是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;lnk&lt;/code&gt; 后缀的文件，这个文件里面指向了如何打开目标文件或文件夹的一些参数，于是当在文件资源管理器中打开快捷方式时，就直接打开了目标文件或文件夹（当然，启动一个程序可能是大多数用法）。&lt;/p&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;重解析点（Reparse Point）自 NTFS v3.0（随 Windows 2000 推出）开始便一直存在于 Windows 操作系统中。除了我们前面提到的可通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 创建的那三种外，还有其他种类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Volume Mount Ppoints&lt;/li&gt;
  &lt;li&gt;Distributed Link Tracking（DLT）&lt;/li&gt;
  &lt;li&gt;Data Deduplication&lt;/li&gt;
  &lt;li&gt;Hierarchical Storage Management（HSM）&lt;/li&gt;
  &lt;li&gt;Native Structured Storage（NSS）&lt;/li&gt;
  &lt;li&gt;Unix Doman Socket（socket）&lt;/li&gt;
  &lt;li&gt;System Compression&lt;/li&gt;
  &lt;li&gt;OneDrive&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;比较&quot;&gt;比较&lt;/h2&gt;

&lt;p&gt;可能单单说名字，你不一定能明白什么时候要用哪一种。于是我将这些链接的不同整理了出来贴在下面。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;硬链接（Hard Link）&lt;/th&gt;
      &lt;th&gt;目录联接（Junction Point）&lt;/th&gt;
      &lt;th&gt;符号链接（Symbolic Link）&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;命令&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /H Link Target&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /J Link Target&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /D Link Target&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;作用&lt;/td&gt;
      &lt;td&gt;为某文件创建别名，可让不同的路径对应同一个文件的数据。&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;链接到文件&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;链接到文件夹&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;需要提升为管理员权限&lt;/td&gt;
      &lt;td&gt;需要&lt;/td&gt;
      &lt;td&gt;不需要&lt;/td&gt;
      &lt;td&gt;通常需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;[坑1]&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;跨驱动器卷（盘符）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️（仅本地计算机）&lt;/td&gt;
      &lt;td&gt;✔️（包括 SMB 文件或路径）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;操作系统支持&lt;/td&gt;
      &lt;td&gt;Windows NT 3.1 开始支持&lt;br /&gt;Windows 2000 开始有 API &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateHardLink()&lt;/code&gt;&lt;br /&gt;Windows NT 6.0 开始能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink /H&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Windows 2000+&lt;/td&gt;
      &lt;td&gt;Windows Vista+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;可链接到不存在的目标&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;可链接到相对目录&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌（可以使用相对路径创建，但创建完即变绝对路径）&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;删除方法&lt;/td&gt;
      &lt;td&gt;del&lt;/td&gt;
      &lt;td&gt;rd&lt;/td&gt;
      &lt;td&gt;rd / del&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;当链接被单独删除后&lt;/td&gt;
      &lt;td&gt;只有所有指向原始文件的硬链接和原始文件全部删除后文件数据才会被删除。&lt;/td&gt;
      &lt;td&gt;Windows Vista 之后原始文件夹不受影响；Windows 2000/XP/2003 会导致原始子文件夹被删除。&lt;/td&gt;
      &lt;td&gt;原始文件夹不受影响。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;当原始文件被单独删除后&lt;/td&gt;
      &lt;td&gt;硬链接依然能正常访问到文件的数据。&lt;/td&gt;
      &lt;td&gt;目录联接失效，指向不存在的目录。&lt;/td&gt;
      &lt;td&gt;符号链接失效，指向不存在的目录。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[坑1]&lt;/code&gt;: 在微软的官方博客中已有说明：从 Windows 10 Insiders build 14972 开始，符号链接对开发者将不再需要管理员权限，这可以让开发者像在 Linux 或 macOS 上一样高效地工作。（通过如下图所示的开关来决定此操作是否需要管理员权限，打开则无需管理员权限。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-09-37-39.png&quot; alt=&quot;开发者模式&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;额外的坑&quot;&gt;额外的坑&lt;/h2&gt;

&lt;p&gt;如果你在开始菜单里面有快捷方式指向了一个目录联接（Junction Point）中的文件，那么在 Windows 10 操作系统更新后这个快捷方式便会消失。目前正在调查消失的原因，如果确认是目录联接的 bug 或者开始菜单的 bug，就将进展报告给微软。&lt;/p&gt;

&lt;p&gt;关于这个 bug，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/lukesampson/scoop/issues/3941&quot;&gt;After the windows 10 updates, all shortcuts of scoop will disappear. · Issue #3941 · lukesampson/scoop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一般来说，阅读本文应该就理解了 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 的正确用法，也不应该会出现我另一篇博客中的情况：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/problems-of-mklink.html&quot;&gt;解决 mklink 使用中的各种坑（硬链接，软链接/符号链接，目录链接） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，附我使用目录联接/符号链接的一些用途：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/collect-nuget-output-folder-for-fast-package-debugging.html&quot;&gt;通过 mklink 收集本地文件系统的所有 NuGet 包输出目录来快速调试公共组件代码 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/NTFS_reparse_point&quot;&gt;NTFS reparse point - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9042542/what-is-the-difference-between-ntfs-junction-points-and-symbolic-links&quot;&gt;windows - What is the difference between NTFS Junction Points and Symbolic Links? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hard_link&quot;&gt;Hard link - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links&quot;&gt;Create symbolic links (Windows 10) - Windows security - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/&quot;&gt;Symlinks in Windows 10! - Windows Developer Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 11 Jun 2020 01:27:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/ntfs-link-comparisons.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/ntfs-link-comparisons.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发</title>
        <description>&lt;p&gt;Windows 10 自 1703 开始引入第二代的多屏 DPI 机制（PerMonitor V2），而 WPF 框架可以支持此第二代的多屏 DPI 机制。&lt;/p&gt;

&lt;p&gt;本文将介绍 WPF 框架利用第二代多屏 DPI 机制进行高 DPI 适配的方法。同时，也介绍低版本的 WPF 或者低版本的操作系统下如何做兼容。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加应用程序清单文件&quot;&gt;添加应用程序清单文件&lt;/h2&gt;

&lt;p&gt;在你现有 WPF 项目的主项目中需要添加两个文件以支持第二代的多屏 DPI 机制。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;app.manifest &lt;em&gt;(决定性文件)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;app.config &lt;em&gt;(修复 Bug, .NET Framework 4.6.2 及以上可忽略)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-18-18-18-31.png&quot; alt=&quot;项目中新增的两个文件&quot; /&gt;&lt;br /&gt;
▲ 项目中新增的两个文件&lt;/p&gt;

&lt;p&gt;默认情况下，app.config 在你创建 WPF 项目的时候就会存在，而 app.manifest 则不是。如果你的项目中已经存在这两个文件，就不需要添加了。&lt;/p&gt;

&lt;h3 id=&quot;如果你没有-appconfig如何添加&quot;&gt;如果你没有 app.config，如何添加？&lt;/h3&gt;

&lt;p&gt;打开项目属性，然后在属性中选择 .NET Framework 的版本，无论你选择哪个，app.config 都会自动为你添加。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-19-11-54-14.png&quot; alt=&quot;选择 .NET Framework 版本以便添加 app.config 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，正统的方法是跟下面的 app.manifest 的添加方法相同，你会在下面看到 Visual Studio 新建项中 app.manifest 和 app.config 文件是挨在一起的。&lt;/p&gt;

&lt;h3 id=&quot;如果你没有-appmanifest如何添加&quot;&gt;如果你没有 app.manifest，如何添加？&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-15-56-38.png&quot; alt=&quot;新建文件的时候选择应用程序清单文件（应用程序配置文件就在旁边）&quot; /&gt;&lt;br /&gt;
▲ 新建文件的时候选择应用程序清单文件（应用程序配置文件就在旁边）&lt;/p&gt;

&lt;h2 id=&quot;了解-wpf-清单文件中的-dpi-感知设置&quot;&gt;了解 WPF 清单文件中的 DPI 感知设置&lt;/h2&gt;

&lt;h3 id=&quot;dpiaware&quot;&gt;DpiAware&lt;/h3&gt;

&lt;p&gt;在你打开了 app.manifest 文件后，找到以下代码，然后取消注释：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
    DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need 
    to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should 
    also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--
&amp;lt;application xmlns=&quot;urn:schemas-microsoft-com:asm.v3&quot;&amp;gt;
&amp;lt;windowsSettings&amp;gt;
    &amp;lt;dpiAware xmlns=&quot;http://schemas.microsoft.com/SMI/2005/WindowsSettings&quot;&amp;gt;true&amp;lt;/dpiAware&amp;gt;
&amp;lt;/windowsSettings&amp;gt;
&amp;lt;/application&amp;gt;
--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面这一段代码是普通的 DPI 感知的清单设置，开启后获得系统 DPI 感知级别（System DPI Awareness）。&lt;/p&gt;

&lt;p&gt;如果要开启 Per-Monitor DPI 感知，将上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm&lt;/code&gt;（pm 表示 per-monitor）。&lt;/p&gt;

&lt;p&gt;不过这只是兼容性的设计而已，感谢老版本的系统使用字符串包含的方式，于是可以老版本的系统可以兼容新的 DPI 感知值：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;什么都不填
    &lt;ul&gt;
      &lt;li&gt;如果你额外也没做什么 DPI 相关的操作，那么就是 Unaware。&lt;/li&gt;
      &lt;li&gt;如果你在程序启动的时候调用了 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 或 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 函数，那么就会按照调用此函数的效果来感知 DPI。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 字符串
    &lt;ul&gt;
      &lt;li&gt;当前进程设置为系统级 DPI 感知（System DPI Awareness）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 字符串
    &lt;ul&gt;
      &lt;li&gt;在 Windows Vista / 7 / 8 中，与什么都不填的效果是一样的。&lt;/li&gt;
      &lt;li&gt;在 Windows 8.1 / 10 中，当前进程设置为不感知 DPI（Unaware），就算你调用了 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 也是没有用的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm&lt;/code&gt; 字符串
    &lt;ul&gt;
      &lt;li&gt;在 Windows Vista / 7 / 8 中，当前进程设置为系统级 DPI 感知（System DPI Awareness）。&lt;/li&gt;
      &lt;li&gt;在 Windows 8.1 / 10 中，当前进程设置为屏幕级 DPI 感知（Per-Monitor DPI Awareness）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;per monitor&lt;/code&gt; 字符串
    &lt;ul&gt;
      &lt;li&gt;在 Windows Vista / 7 / 8 中，与什么都不填的效果是一样的。&lt;/li&gt;
      &lt;li&gt;在 Windows 8.1 / 10 中，当前进程设置为屏幕级 DPI 感知（Per-Monitor DPI Awareness）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;其他任何字符串
    &lt;ul&gt;
      &lt;li&gt;在 Windows Vista / 7 / 8 中，与什么都不填的效果是一样的。&lt;/li&gt;
      &lt;li&gt;在 Windows 8.1 / 10 中，当前进程设置为不感知 DPI（Unaware），就算你调用了 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 也是没有用的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;说明一下，&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 是新 API，要求的最低系统版本是 Windows 8.1，调用这个才能指定为 Per-Monitor 的 DPI 感知。而 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 是 Vista 开始引入的老 API，没有参数可以传。&lt;/p&gt;

&lt;h3 id=&quot;dpiawareness&quot;&gt;DpiAwareness&lt;/h3&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;asmv3:application&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;asmv3:windowsSettings&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/SMI/2016/WindowsSettings&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dpiAwareness&amp;gt;&lt;/span&gt;PerMonitorV2, unaware&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dpiAwareness&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/asmv3:windowsSettings&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/asmv3:application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意：&lt;strong&gt;只有 Windows 10 (1607) 及以上版本才会识别此节点的 DPI 设置&lt;/strong&gt;。如果你设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 节点，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 就会被忽略。&lt;/p&gt;

&lt;p&gt;建议你可以两个节点都指定，这样既可以使用到 Windows 10 1607 的新特性，又可以兼容老版本的 Windows 操作系统。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 节点支持设置一个或多个 DPI 感知级别，用逗号分隔。如果你指定了多个，那么操作系统会从第一个开始识别，如果能识别就使用，否则会找第二个。用这种方式，未来的应用可以指定当前系统不支持的 DPI 感知级别。&lt;/p&gt;

&lt;p&gt;鉴于此，在目前 Windows 7 还大行其道的今天，为了兼容，&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 都设置是比较靠谱的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 节点目前支持的值有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;什么都不设置
    &lt;ul&gt;
      &lt;li&gt;按 &lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 节点的结果来&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;整个逗号分隔的序列都没有能识别的 DPI 感知级别
    &lt;ul&gt;
      &lt;li&gt;如果你额外也没做什么 DPI 相关的操作，那么就是 Unaware。&lt;/li&gt;
      &lt;li&gt;如果你在程序启动的时候调用了 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 或 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 函数，那么就会按照调用此函数的效果来感知 DPI。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;第一个能识别的感知级别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;system&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;当前进程设置为系统级 DPI 感知（System DPI Awareness）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;第一个能识别的感知级别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;permonitor&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;当前进程设置为屏幕级 DPI 感知（Per-Monitor DPI Awareness）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;第一个能识别的感知级别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;permonitorv2&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;当前进程设置为第二代屏幕级 DPI 感知（Per-Monitor V2 DPI Awareness）。&lt;/li&gt;
      &lt;li&gt;仅在 Windows 10 (1703) 及以上版本才可被识别&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;第一个能识别的感知级别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;unaware&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;当前进程设置为不感知 DPI（Unaware），就算你调用了 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness&quot;&gt;SetProcessDpiAwareness&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiaware?wt.mc_id=MVP&quot;&gt;SetProcessDPIAware&lt;/a&gt; 也是没有用的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使-wpf-程序支持-per-monitor-v2-级-dpi-感知&quot;&gt;使 WPF 程序支持 Per-Monitor V2 级 DPI 感知&lt;/h2&gt;

&lt;p&gt;前面我们分析 App.Manifest 文件中 DPI 的设置后，几乎得到一个信息，&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 都是要设置的，除非以后绝大多数用户的系统版本都到达 Windows 10 (1607) 及以上。&lt;/p&gt;

&lt;p&gt;以下是推荐的 DPI 感知级别设置：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;windowsSettings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- The combination of below two tags have the following effect : 
         1. Per-Monitor for &amp;gt;= Windows 10 Anniversary Update
         2. System &amp;lt; Windows 10 Anniversary Update --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dpiAwareness&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/SMI/2016/WindowsSettings&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;PerMonitorV2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dpiAwareness&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dpiAware&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/SMI/2005/WindowsSettings&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dpiAware&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/windowsSettings&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意：系统版本在 Windows 10 (1703) 或以上，V2 的感知级别才会生效，否则就直接使用系统级 DPI 感知。&lt;/p&gt;

&lt;p&gt;这里我们其实偷懒了，这种写法方便我们仅处理两种不同的 DPI 缩放规则：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Windows 10 (1703) 之后的系统，全按最全支持来做兼容；&lt;/li&gt;
  &lt;li&gt;其他系统，全按 Windows 7 的支持级别来做兼容。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你可能注意到本文文末的参考文章中有微软的官方博客，里面推荐的是支持所有级别的 DPI 感知。这看你的需求，因为部分 DPI 相关的模块如果你打算都支持，可能需要更加复杂的判定和计算。本文推荐的少一些，省一点开发量（反正 Windows 8.1 和 Windows 10 早期版本的用户量太少，这部分用户体验不比 Windows 7 差，又不是不能用）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-16-41-32.png&quot; alt=&quot;又不是不能用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第一代和第二代的 Per-Monitor 感知之间的差异，可以参考：&lt;a href=&quot;/post/windows-high-dpi-development&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;额外的，如果你的 .NET Framework 版本在 .NET Framework 4.6.2 以下，但操作系统在 Windows 10 及以上，你还需要修改 App.config 文件（在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;configuration /&amp;gt;&lt;/code&gt; 节点）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;runtime&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppContextSwitchOverrides&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value =&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Switch.System.Windows.DoNotScaleForDpiChanges=false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/runtime&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这个值要设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。（微软官方吐槽：Yes, set it to false. Double negative FTW!）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AppContextSwitchOverrides&lt;/code&gt; 不能被设置两次，如果一已经设置了其他值，需要用分号分隔多个值。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;特别说明，当面向 .NET Framework 4.6.2 以下版本编译，但运行于 Windows 10 (1607) 和以上版本时，只需要添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;Switch.System.Windows.DoNotScaleForDpiChanges=false&lt;/code&gt; 即可让 WPF 程序处理 Dpi Change 消息，此时 WPF 程序就像高版本的 .NET Framework 中一样能够正常处理多屏下的 DPI 缩放。&lt;/p&gt;

&lt;p&gt;以上，划重点 &lt;strong&gt;你并不需要编译为高版本的 .NET Framework 即可获得 Per-Monitor 的 DPI 缩放支持&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;wpf-程序在特殊清单设置下的效果&quot;&gt;WPF 程序在特殊清单设置下的效果&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 不设置，&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 节点设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-17-28-31.png&quot; alt=&quot;100% DPI&quot; /&gt;&lt;br /&gt;
▲ 100% DPI&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-17-28-52.png&quot; alt=&quot;150% DPI&quot; /&gt;&lt;br /&gt;
▲ 150% DPI&lt;/p&gt;

&lt;p&gt;注意到标题栏（非客户区）没有缩放，而 WPF 区域（客户区）清晰地缩放了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 不设置，&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 节点设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-17-28-31.png&quot; alt=&quot;100% DPI&quot; /&gt;&lt;br /&gt;
▲ 100% DPI&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-17-30-59.png&quot; alt=&quot;150% DPI&quot; /&gt;&lt;br /&gt;
▲ 150% DPI&lt;/p&gt;

&lt;p&gt;注意到标题栏（非客户区）被缩放了，而 WPF 区域（客户区）被 DPI 虚拟化进行了位图拉伸（模糊）。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 不设置，&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAware&lt;/code&gt; 节点设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm12345&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;此时，WPF 程序无法启动！！！而你只需要减少一位数字，例如写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm1234&lt;/code&gt; 即可成功启动，效果跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 是一样的，注意效果 &lt;strong&gt;不是&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;true/pm&lt;/code&gt;。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;/pm&lt;/code&gt; 并没有显示出它的含义来。额外的，如果设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 但后面跟随那么长的字符串，WPF 程序是可以启动的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dpiAwareness&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;PerMonitorV2&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-22-17-37-33.png&quot; alt=&quot;150% DPI&quot; /&gt;&lt;br /&gt;
▲ 150% DPI&lt;/p&gt;

&lt;p&gt;注意到标题栏（非客户区）被缩放了，而 WPF 区域（客户区）也能清晰地缩放（仅 Windows 10 1703 及以上系统才是这个效果）。&lt;/p&gt;

&lt;h2 id=&quot;低版本-net-framework-和-低版本-windows-下的-wpf-dpi-缩放&quot;&gt;低版本 .NET Framework 和 低版本 Windows 下的 WPF DPI 缩放&lt;/h2&gt;

&lt;p&gt;由于 Windows 8.1 操作系统用户存量不多，主要是 Windows 7 和 Windows 10。所以我们要么兼容完全不支持 Per-Monitor 的 Windows 7，要么使用具有新特性的 Windows 10 即可获得最佳的开发成本平衡。&lt;strong&gt;使用以上的 DPI 缩放方法足以让你的 WPF 应用在任何一个 .NET Framework 版本下获得针对屏幕的 DPI 清晰缩放（Per-Monitor DPI Awareness）。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;所以仅针对 Windows 8.1 做特殊的 DPI 缩放是不值得的，把 Windows 8.1 当做 Windows 7 来做那种不支持 Per-Monitor 的处理就好了。当然你硬要支持也有相关文档可以看：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/hidpi/declaring-managed-apps-dpi-aware&quot;&gt;Developing a Per-Monitor DPI-Aware WPF Application - Microsoft Docs&lt;/a&gt; 了解实现方法。具体是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DisableDpiAwareness&lt;/code&gt; 特性和 &lt;a href=&quot;https://code.msdn.microsoft.com/windowsdesktop/Per-Monitor-Aware-WPF-e43cde33?wt.mc_id=MVP&quot;&gt;Windows Per-Monitor Aware WPF Sample&lt;/a&gt; 中的源码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/hidpi/declaring-managed-apps-dpi-aware?wt.mc_id=MVP&quot;&gt;Developing a Per-Monitor DPI-Aware WPF Application - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/WPF-Samples/blob/master/PerMonitorDPI/Developer%20Guide%20-%20Per%20Monitor%20DPI%20-%20WPF%20Preview.docx&quot;&gt;WPF-Samples/Developer Guide - Per Monitor DPI - WPF Preview.docx at master · Microsoft/WPF-Samples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/sbscs/application-manifests?wt.mc_id=MVP&quot;&gt;Application Manifests - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/&quot;&gt;High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update (1703) - Windows Developer Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 10 Jun 2020 08:41:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-high-dpi-development-for-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-high-dpi-development-for-wpf.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后，可以运行 VMware Workstation</title>
        <description>&lt;p&gt;VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后，可以运行 VMware Workstation。有关更多详细信息，请访问 &lt;a href=&quot;http://www.vmware.com/go/turnoff_CG_DG&quot;&gt;http://www.vmware.com/go/turnoff_CG_DG&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我在系统升级到 Windows 10 2004 后，启动 VMware 的任一台虚拟机时会弹出错误提示框：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-08-08-53-37.png&quot; alt=&quot;不兼容&quot; /&gt;&lt;/p&gt;

&lt;p&gt;嗯，图标题中的“lindexi”就是小伙伴&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt;；他在我的电脑上运行了一台虚拟机远程使用。然而怎么能随随便便就让虚拟机挂掉呢？得在他醒来之前偷偷修好。&lt;/p&gt;

&lt;p&gt;提示框中的 Device/Credential Guard 就是 Windows 10 系统的“内核隔离”。&lt;/p&gt;

&lt;p&gt;按照以下步骤逐一执行，直到修复。&lt;/p&gt;

&lt;h2 id=&quot;特别前提&quot;&gt;特别前提&lt;/h2&gt;

&lt;p&gt;VMware 从 15.5.5 版本开始，已支持在启用了 Hyper-V 的 Windows 10 主机上运行：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用 WSL 和 Device/Credential Guard 等功能时，用户仍可运行 VMware Workstation 虚拟机&lt;/li&gt;
  &lt;li&gt;需要 Windows 10 20H1 版本及以上系统&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此，如果你觉得不想折腾，直接将 VMware 升级到 15.5.5 以上即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-08-55-34.png&quot; alt=&quot;15.5.5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图来自于 &lt;a href=&quot;https://blog.sdlsj.net/&quot;&gt;kkwpsv（李少珺）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;第一步关闭内核隔离然后重启&quot;&gt;第一步：关闭内核隔离，然后重启&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-08-09-03-28.png&quot; alt=&quot;关闭内核隔离&quot; /&gt;&lt;/p&gt;

&lt;p&gt;要找到“内核隔离”开关，直接在开始菜单搜索“内核隔离”或者“Credential Guard”即可。如果搜不到，去任务栏右下角找到“Windows 安全中心”双击打开。&lt;/p&gt;

&lt;h2 id=&quot;第二步禁用设备防护&quot;&gt;第二步：禁用设备防护&lt;/h2&gt;

&lt;p&gt;打开“组策略”，进入 本地计算机策略 -&amp;gt; 计算机配置 -&amp;gt; 管理模板 -&amp;gt; 系统 -&amp;gt; Device Guard -&amp;gt; 基于虚拟化的安全性。&lt;/p&gt;

&lt;p&gt;选择已禁用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-08-41-37.png&quot; alt=&quot;禁用基于虚拟化的安全性&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步关闭-hyper-v&quot;&gt;第三步：关闭 Hyper-V&lt;/h2&gt;

&lt;p&gt;在“启用或关闭 Windows 功能”里，关闭掉 Hyper-V 虚拟机（也需要重启）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-10-08-48-21.png&quot; alt=&quot;关闭 Hyper-V&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第四步运行命令&quot;&gt;第四步：运行命令&lt;/h2&gt;

&lt;p&gt;以管理员身份运行以下命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;bcdedit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hypervisorlaunchtype&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后重启计算机。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://bbs.pcbeta.com/forum.php?mod=viewthread&amp;amp;tid=1813168&amp;amp;page=1#pid49133290&quot;&gt;Windows沙盒和vmware workstation似乎只能存在一个-远景论坛-微软极客社区&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 10 Jun 2020 00:58:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/vmware-wants-to-disable-credential-guard.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/vmware-wants-to-disable-credential-guard.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Visual Studio 智能感知提示里的奇妙行为一览</title>
        <description>&lt;p&gt;本文就是个吐槽，感兴趣一一去报给微软。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;自动完成与建议完成&quot;&gt;自动完成与建议完成&lt;/h2&gt;

&lt;p&gt;作为本文的背景，你可能需要了解 Visual Studio 有一个设置：“在建议完成模式和标准完成模式之间切换”。就是下面这个按钮，你可以在工具条上找到。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-14-28-25.png&quot; alt=&quot;建议完成和标准完成&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你一定很差异，看着这翻译，你知道“选中”对应“建议完成模式”还是“标准完成模式”呢？你知道什么是建议完成模式，什么是标准完成模式吗？&lt;/p&gt;

&lt;p&gt;不要紧，这不是翻译的问题，因为英文原文也一样令人摸不着头脑。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-14-38-08.png&quot; alt=&quot;suggestion and standard completion modes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么这件事就交给本文来处理吧！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-10-16.png&quot; alt=&quot;用什么模式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上图所示，如果不选中那个图标，那么智能感知会更智能，默认回车即上屏，而不用再打字母了。所以我更&lt;strong&gt;倾向于不要选中那个图标&lt;/strong&gt;。至于那个图标到底哪个是建议完成模式哪个是标准完成模式，已经不重要了（反正就算知道了，名字也不直观）。&lt;/p&gt;

&lt;h2 id=&quot;奇妙行为&quot;&gt;奇妙行为&lt;/h2&gt;

&lt;p&gt;接下来我们的讨论中，前面所述的图标都&lt;strong&gt;不选中&lt;/strong&gt;，这样能最大程度体现智能感知的智能程度。&lt;/p&gt;

&lt;h3 id=&quot;带星-vs-不带星&quot;&gt;带星 V.S. 不带星&lt;/h3&gt;

&lt;p&gt;大家知道 Visual Studio 2019 开始，微软开始基于机器学习来训练智能感知的建议。凡通过机器学习建议的项都会用“★”（星号）标注。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-18-55.png&quot; alt=&quot;带星的建议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，一旦没有任何项带星，那么就没有项被选中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-23-15.png&quot; alt=&quot;无带星的建议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;奇妙行为：有机器学习带星的项才会自动选中，没有带星的项就不会自动选中。&lt;/p&gt;

&lt;h3 id=&quot;全小写-vs-部分大写&quot;&gt;全小写 V.S. 部分大写&lt;/h3&gt;

&lt;p&gt;注意看下面的图片中的提示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-25-37.png&quot; alt=&quot;大小写&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;preso&lt;/code&gt; 能自动选中 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Preso&lt;/code&gt; 则不会自动选中 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;奇妙行为：如果完全用小写字母打字，则可以按词匹配前几位字母；如果出现了大写字母，那么出现的小写字母就默认不算做新单词。&lt;/p&gt;

&lt;h3 id=&quot;直接输入-vs-删除后输入&quot;&gt;直接输入 V.S. 删除后输入&lt;/h3&gt;

&lt;p&gt;看看下面的动图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-adding-or-removing-letters-for-intellisense.gif&quot; alt=&quot;大小写&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意到了什么？如果是输入单词，那么会默认选中；但删除了字母后再输入单词，就不会默认选中了。&lt;/p&gt;

&lt;p&gt;奇妙行为：直接输入和删除后再输入，默认选中的行为不同；删除后再输入不会默认选中。&lt;/p&gt;

&lt;h3 id=&quot;输入单词-vs-输入点&quot;&gt;输入单词 V.S. 输入点&lt;/h3&gt;

&lt;p&gt;从前面的示例可以看到，如果能满足 Visual Studio 的输入要求输入单词（鬼知道是什么要求），那么基本能直接选中。但按要求输入点“.”却不一定能直接选中。&lt;/p&gt;

&lt;p&gt;所以，如果你能熟练摸透 VS 单词的补全逻辑，那么可能仅在输入点“.”的时候才会遇到有时选中有时不选中的问题。&lt;/p&gt;

&lt;p&gt;奇妙行为：？&lt;/p&gt;

&lt;h3 id=&quot;不同种类的空格&quot;&gt;不同种类的空格&lt;/h3&gt;

&lt;p&gt;当即将输入的代码是枚举这种很封闭的语句时，直接自动选中（虽然自动提示的经常不是想要的）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-44-02.png&quot; alt=&quot;自动选中&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而当输入的代码是命名这种比较开放的语句时，不会自动选中（虽然自动提示的名字很想要）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-09-15-44-47.png&quot; alt=&quot;不会自动选中&quot; /&gt;&lt;/p&gt;

&lt;p&gt;奇妙行为：同样是空格，有时会自动选中，有时不会自动选中。&lt;/p&gt;

&lt;h2 id=&quot;吐槽&quot;&gt;吐槽&lt;/h2&gt;

&lt;p&gt;可以发现，虽然 Visual Studio 提供了一个是否自动选中智能感知提示选项的开关，但这个开关的行为并不总能反映出实际的使用效果。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果选中了按钮（见本文开头所述），则一定不会自动选中智能感知提示&lt;/li&gt;
  &lt;li&gt;如果没选中按钮，则是否自动选中智能感知提示&lt;strong&gt;简直随缘&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你要说，Visual Studio 猜得准的时候会自动选中，猜不准的时候不会自动选中。但实际使用效果却是，你自认为猜得准的时候，我却不需要你的提示；你自认为猜不准的时候，给出的建议却非常完美。不如直接按照开关来，简单利索，还不用在开发的时候琢磨到底该不该按上下来一轮选择。&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Jun 2020 07:52:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fantastic-intellisense-behavior-of-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fantastic-intellisense-behavior-of-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>Chrome 窗口全黑了，不怕，有招</title>
        <description>&lt;p&gt;Chrome 的窗口偶尔会出现全黑掉的情况。从轻微的到严重的，本文都有解决方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;偶然全黑&quot;&gt;偶然全黑&lt;/h2&gt;

&lt;p&gt;Chrome 全黑的状态看起来像下图这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-08-08-08-05.png&quot; alt=&quot;Chrome 全黑&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通常发生在这些时间点后：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;锁屏解锁后&lt;/li&gt;
  &lt;li&gt;突然间显卡驱动崩溃了一下&lt;/li&gt;
  &lt;li&gt;显卡驱动刚刚升完级&lt;/li&gt;
  &lt;li&gt;远程桌面连接后&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这时，通常还伴随着基于 Chromium 内核的应用全部黑屏，比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visual Studio Code (VSCode)&lt;/li&gt;
  &lt;li&gt;Microsoft Edge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;开一个新窗口&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;开的方法有（任选其一，适用与所有 Chromium 内核应用）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在任务栏上右键，重新点开程序（看下图）&lt;/li&gt;
  &lt;li&gt;直接鼠标中键点击任务栏上的图标&lt;/li&gt;
  &lt;li&gt;去开始菜单或者其他入口处点开程序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-06-08-08-14-50.png&quot; alt=&quot;新开一个窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;一直黑&quot;&gt;一直黑&lt;/h2&gt;

&lt;p&gt;有时，前面的方法并不能帮你解决问题——新开程序也依然是黑屏的。&lt;/p&gt;

&lt;p&gt;通常发生在这些时间点后：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Windows 10 系统更新完后&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这时，你可能发现其他基于 Chromium 内核的应用是正常的。也就说明此时的问题仅仅是 Chrome 浏览器的问题。&lt;/p&gt;

&lt;p&gt;解决方法是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;%LOCALAPPDATA%\Google\Chrome\User Data\ShaderCache\GPUCache&lt;/code&gt; 下的全部文件&lt;/li&gt;
  &lt;li&gt;重新启动 Chrome 浏览器&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;例如，我的文件夹是 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\AppData\Local\Google\Chrome\User Data\ShaderCache\GPUCache&lt;/code&gt;，全部删除后重新启动即恢复。&lt;/p&gt;
</description>
        <pubDate>Mon, 08 Jun 2020 00:22:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-fix-the-pure-black-window-of-chrome.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-fix-the-pure-black-window-of-chrome.html</guid>
        
        
        <category>windows</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>拿别人的 Program Files 文件夹？别忘了考虑 x86/x64 路径</title>
        <description>&lt;p&gt;要拿适用于自己进程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program Files&lt;/code&gt; 文件夹很简单，无脑拿就好了。不过，如果涉及到拿其他程序的，那么就会涉及到与其他程序不同架构时路径不同的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)&lt;/code&gt; 即可用来获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program Files&lt;/code&gt; 文件夹的路径。从 .NET Framework 4.0 开始，还增加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProgramFilesX86&lt;/code&gt; 枚举可用。&lt;/p&gt;

&lt;p&gt;在官方文档中，&lt;code class=&quot;highlighter-rouge&quot;&gt;ProgramFiles&lt;/code&gt; 枚举拿的是当前进程架构下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program Files&lt;/code&gt; 文件夹，&lt;code class=&quot;highlighter-rouge&quot;&gt;ProgramFilesX86&lt;/code&gt; 拿的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;x86&lt;/code&gt; 进程架构下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program Files&lt;/code&gt; 文件夹。&lt;/p&gt;

&lt;p&gt;为了具体说明，可以用下面的示例程序：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is64Bit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is64BitProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfx86&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFolderPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpecialFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgramFilesX86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFolderPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpecialFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgramFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;process = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is64Bit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;x64&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;x86&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;x86     = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfx86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;current = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 x64 系统下，输出是：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;x64&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;x86&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;rogram Files (x86)&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;rogram Files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 x86 系统下，输出是：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;x86&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;x86&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;rogram Files (x86)&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;rogram Files (x86)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以，只是通过此属性的话，x86 进程不能获取到 x64 进程的目录。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.environment.specialfolder&quot;&gt;Environment.SpecialFolder Enum (System) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/194157/c-sharp-how-to-get-program-files-x86-on-windows-64-bit&quot;&gt;C# - How to get Program Files (x86) on Windows 64 bit - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 03 Jun 2020 00:13:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-program-files-cross-x64-x86.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-program-files-cross-x64-x86.html</guid>
        
        
        <category>dotnet</category>
        
        <category>win32</category>
        
      </item>
    
      <item>
        <title>在 .NET 对象和 JSON 互相序列化的时候，枚举类型如何设置成字符串序列化，而不是整型？</title>
        <description>&lt;p&gt;默认情况下，Newtonsoft.Json 库序列化和反序列化 JSON 到 .NET 类型的时候，对于枚举值，使用的是整数。然而，在公开 JSON 格式的 API 时，整数会让 API 不易于理解，也不利于扩展和兼容。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;那么，如何能使用字符串来序列化和反序列化 JSON 对象中的枚举呢？&lt;/p&gt;

&lt;p&gt;—— 使用转换器（&lt;code class=&quot;highlighter-rouge&quot;&gt;JsonConverter&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;Newtonsoft.Json 中自带了一些转换器，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Newtonsoft.Json.Converters&lt;/code&gt; 命名空间下。其中枚举的转换是 &lt;code class=&quot;highlighter-rouge&quot;&gt;StringEnumConverter&lt;/code&gt;，我们只需要将其标记在属性上即可。&lt;/p&gt;

&lt;p&gt;如下面的代码所示：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json.Converters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Models&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;JsonConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringEnumConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubiLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubiLevel&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ABit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Very&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Extreme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于“逗比程度”枚举，增加了转换器后，这个对象的序列化和反序列化将成：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;very&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;StringEnumConverter&lt;/code&gt; 后面的参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表示使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;camelCase&lt;/code&gt; 来格式化命名，即首字母小写。&lt;/p&gt;

&lt;p&gt;当然，如果你希望属性名也小写的化，需要加上额外的序列化属性：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  using System.Runtime.Serialization;
&lt;/span&gt;    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;
    ……

++      [DataContract]
        public class Foo
        {
&lt;span class=&quot;gi&quot;&gt;++          [DataMember(Name = &quot;level&quot;)]
&lt;/span&gt;            [JsonConverter(typeof(StringEnumConverter), true)]
            public DoubiLevel Level { get; set; }
        }
    ……
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将序列化和反序列化成：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;very&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 02 Jun 2020 23:57:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/newtonsoft-json-convert-enum-as-strings.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/newtonsoft-json-convert-enum-as-strings.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何在保留原本所有样式/绑定和用户设置值的情况下，设置和还原 WPF 依赖项属性的值</title>
        <description>&lt;p&gt;WPF 备份某控件的一些属性，做一些神奇的操作，然后再还原这些属性。多么司空见惯的操作呀！然而怎么备份却是值得研究的问题。直接赋值？那一定是因为你没踩到一些坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;场景和问题&quot;&gt;场景和问题&lt;/h2&gt;

&lt;p&gt;现在，我们假想一个场景（为了编代码方便）：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有一个窗口，设置了一些样式属性&lt;/li&gt;
  &lt;li&gt;现在需要将这个窗口设置为全屏，这要求修改一些原来的属性（WPF 自带那设置有 bug，我会另写一篇博客说明）&lt;/li&gt;
  &lt;li&gt;取消设置窗口全屏后，之前修改的那些属性要“完美”还原&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;一般可能会这么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_oldStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnEnterFullScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_oldStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnExitFullScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_oldStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果某人在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle&lt;/code&gt; 上设了个动态的样式怎么办？——那当然是不再动态了呀（因为覆盖了样式值）&lt;/li&gt;
  &lt;li&gt;如果某人在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle&lt;/code&gt; 上设置了绑定怎么办？——那当然也是不再生效了呀（因为绑定被你覆盖了）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;解决方法和原理&quot;&gt;解决方法和原理&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;因为各大 WPF 入门书籍都说到了 WPF 依赖项属性的优先级机制，所以大家应该基本都知道这个。不了解的，可以立刻去这里看看：[依赖项属性值优先级 - WPF&lt;/td&gt;
      &lt;td&gt;Microsoft Docs](https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/advanced/dependency-property-value-precedence#dependency-property-setting-precedence-list)。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;是这样的优先级：强制 &amp;gt; 动画 &amp;gt; 本地值 &amp;gt; 模板 &amp;gt; 隐式样式 &amp;gt; 样式触发器 &amp;gt; 模板触发器 &amp;gt; 样式 &amp;gt; 默认样式 &amp;gt; 属性继承 &amp;gt; 元数据默认值。&lt;/p&gt;

&lt;p&gt;而我们通过在 XAML 或 C# 代码中直接赋值，设置的是“本地值”。因此，如果设置了本地值，那么更低优先级的样式当然就全部失效了。&lt;/p&gt;

&lt;p&gt;那么绑定呢？绑定在依赖项属性优先级中并不存在。绑定实际上是通过“本地值”来实现的，将一个绑定表达式设置到“本地值”中，然后在需要值的时候，会 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProvideValue&lt;/code&gt; 提供值。所以，如果再设置了本地值，那么绑定的设置就被覆盖掉了。&lt;/p&gt;

&lt;p&gt;但是，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetCurrentValue&lt;/code&gt; 就是干这件事的！&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetCurrentValue&lt;/code&gt; 设计为在不改变依赖项属性任何已有值的情况下，设置属性当前的值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCurrentValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，只需要还原 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetCurrentValue&lt;/code&gt; 所做的修改，就还原了此依赖项属性的一切设置的值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClearValue&lt;/code&gt;，那会清除本地值。&lt;/p&gt;

&lt;p&gt;然而还差一点，绑定如果在你应用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetCurrentValue&lt;/code&gt; 期间有改变，那么这次的赋值并不会让绑定立即生效，所以我们还需要手工再让绑定重新更新值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBindingExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，综合起来，本文一开始的代码将更新成如下形式：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnEnterFullScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCurrentValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnExitFullScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBindingExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyleProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;延伸&quot;&gt;延伸&lt;/h2&gt;

&lt;p&gt;将代码变得通用一点：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ApplyTempProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCurrentValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RestoreProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBindingExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 02 Jun 2020 23:13:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/change-and-restore-wpf-dependency-value-without-disabling-the-declared-use-of-the-property.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/change-and-restore-wpf-dependency-value-without-disabling-the-declared-use-of-the-property.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：如何管理 Unity 项目中的 NuGet 包？使用第三方 NuGet 包管理器——NuGetForUnity</title>
        <description>&lt;p&gt;Unity 项目虽然可使用 C# 项目作为脚本，却并没有提供一种类似 NuGet 的第一方包管理器。不过，还是有第三方包管理器可以用，为 C# 脚本应用现有的库提供方便。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;nugetforunity&quot;&gt;NuGetForUnity&lt;/h2&gt;

&lt;p&gt;第三方适用于 Unity 的 NuGet 包管理器推荐：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GlitchEnzo/NuGetForUnity&quot;&gt;GlitchEnzo/NuGetForUnity: A NuGet Package Manager for Unity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;去它的 &lt;a href=&quot;https://github.com/GlitchEnzo/NuGetForUnity/releases&quot;&gt;Release 页面&lt;/a&gt;，可以下载到 NuGetForUnity.2.0.0.unitypackage 的 Unity 包文件。&lt;/p&gt;

&lt;h2 id=&quot;安装-nugetforunity&quot;&gt;安装 NuGetForUnity&lt;/h2&gt;

&lt;p&gt;NuGetForUnity 是按项目安装的，所以你需要先打开一个项目（否则双击安装只会进到项目选择界面）。&lt;/p&gt;

&lt;p&gt;打开了一个 Unity 的项目后，双击下载下来的 NuGetForUnity.2.0.0.unitypackage 文件，你会看到包导入界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-14-56-18.png&quot; alt=&quot;导入包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击 Import 按钮即可将 NuGetForUnity 安装到你刚刚打开的项目中。&lt;/p&gt;

&lt;h2 id=&quot;使用-nugetforunity&quot;&gt;使用 NuGetForUnity&lt;/h2&gt;

&lt;p&gt;安装完 NuGetForUnity 后，你能在 Unity 编辑器的主菜单上面看到 NuGet 入口了。这很像是 Visual Studio 中自带的 NuGet 包管理器，不过这是适用于 Unity 的第三方 NuGet 包管理器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-14-57-26.png&quot; alt=&quot;NuGetForUnity 的界面&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;安装-nuget-包&quot;&gt;安装 NuGet 包&lt;/h3&gt;

&lt;p&gt;就从上面所述的菜单那里打开，你可以进入 NuGet 包的搜索与安装界面。输入并找到你想安装的 NuGet 包，然后点击 Install 即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-15-06-06.png&quot; alt=&quot;搜索与安装 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;还原-nuget-包&quot;&gt;还原 NuGet 包&lt;/h3&gt;

&lt;p&gt;正常情况下，你打开别人上传到版本管理中的仓库后，仅仅启动 Unity 编辑器就可以完成 NuGet 包的还原。因为 NuGetForUnity 是安装到项目当中的，Unity 编辑器启动的时候也会运行 NuGetForUnity，这时就会自动还原项目当中所安装过的 NuGet 包了。&lt;/p&gt;

&lt;h2 id=&quot;还有没有其他包管理方案&quot;&gt;还有没有其他包管理方案？&lt;/h2&gt;

&lt;p&gt;在微软的 &lt;docs.microsoft.com&gt; 文档中，描述 NuGet 包安装的方法是手工的，对于普通的没有依赖的 NuGet 包来说问题不大，不过如果 NuGet 包包含依赖的话，那手工处理的工作量就有点大了，尤其是依赖有嵌套，出现层层嵌套的依赖的时候，几乎可以不用考虑手工安装 NuGet 包的方式了。&lt;/docs.microsoft.com&gt;&lt;/p&gt;

&lt;p&gt;关于手工安装 NuGet 包的方式，我在另一篇入门文档当中也有说到过：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-reference-dlls-and-add-nuget-package-for-unity-csharp-projects.html&quot;&gt;Unity3D 入门：为 Unity 的 C# 项目添加 dll 引用或安装 NuGet 包 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GlitchEnzo/NuGetForUnity&quot;&gt;GlitchEnzo/NuGetForUnity: A NuGet Package Manager for Unity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 01 Jun 2020 01:21:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/third-party-unity-nuget-management.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/third-party-unity-nuget-management.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：Unity 项目版本管理建议使用的 .gitignore 忽略文件和 .gitattributes 文件（2020年4月更新）</title>
        <description>&lt;p&gt;Unity3D 项目虽然说使用 C# 脚本开发，但毕竟不是 .NET/C# 项目。今天一位小伙伴告诉我说用错了 .gitignore 文件，结果管理仓库时丢了很多重要的文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;实际上，GitHub 官方提供了大量不同项目类型的 .gitignore 文件，并且有广大的社区支持时时更新，直接前往 GitHub 官网下载是最好的选择了。&lt;/p&gt;

&lt;p&gt;适用于 Unity 项目的 .gitignore 模板：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/github/gitignore/blob/master/Unity.gitignore&quot;&gt;gitignore/Unity.gitignore at master · github/gitignore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;language-re&quot;&gt;# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
#
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/

# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/

# Asset meta data should only be ignored when the corresponding asset is also ignored
!/[Aa]ssets/**/*.meta

# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*

# Autogenerated Jetbrains Rider plugin
/[Aa]ssets/Plugins/Editor/JetBrains*

# Visual Studio cache directory
.vs/

# Gradle cache directory
.gradle/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta

# Unity3D generated file on crash reports
sysinfo.txt

# Builds
*.apk
*.unitypackage

# Crashlytics generated file
crashlytics-build.properties

# Packed Addressables
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*

# Temporary auto-generated Android Assets
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意到 sln 和 csproj 都是忽略的文件吗？这是 Unity 的项目，其他 .NET 项目经常使用的 sln 解决方案管理方式在这里只是辅助手段而已，你可以阅读我的另一篇博客了解更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-starter-the-sln-and-csproj-files.html&quot;&gt;Unity3D 入门：使用 Visual Studio 开发 Unity C# 脚本，说说根目录的那些 sln 和 csproj 文件 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，你可能注意到 Unity 项目里面有大量的 *.meta 文件，是自动生成的，这个要加入到版本管理吗？答案是&lt;strong&gt;需要&lt;/strong&gt;。Unity 创建这些文件是为了给导入的文件添加额外的元数据信息的，毕竟不能直接修改原来的文件。这些信息包含：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;描述如何导入这个资产文件&lt;/li&gt;
  &lt;li&gt;如何在项目中准备资产&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你删除了这些文件，那么 Unity 会用默认的元数据信息生成一个对应的 .meta 文件。&lt;/p&gt;

&lt;p&gt;其他类型的 .gitignore 文件前往 GitHub 的 gitignore 仓库：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/github/gitignore&quot;&gt;github/gitignore: A collection of useful .gitignore templates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，开启一个新的 Unity 项目时，放一个适用于大多数场景的 .gitattributes 也可以一开始规避一些坑。你可以从下面这位大佬拿到他整理的一个 .gitattribute 文件。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/nemotoo/b8a1c3a0f1225bb9231979f389fd4f3f&quot;&gt;.gitattributes for Unity3D with git-lfs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/42471800/6233938&quot;&gt;unity5 - What is a .meta file and why does Unity create them for all of my assets? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 01 Jun 2020 01:21:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-git-ignore-and-git-attributes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-git-ignore-and-git-attributes.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>解决包含 GitHub Actions Workflow 的分支无法推送的问题</title>
        <description>&lt;p&gt;refusing to allow an OAuth App to create or update workflow &lt;code class=&quot;highlighter-rouge&quot;&gt;{0}&lt;/code&gt; without &lt;code class=&quot;highlighter-rouge&quot;&gt;workflow&lt;/code&gt; scope.&lt;/p&gt;

&lt;p&gt;GitHub 推送失败？试试本文方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;试图向 GitHub 推送一个分支的时候，出现错误 &lt;code class=&quot;highlighter-rouge&quot;&gt;refusing to allow an OAuth App to create or update workflow &lt;/code&gt;{0}&lt;code class=&quot;highlighter-rouge&quot;&gt; without &lt;/code&gt;workflow&lt;code class=&quot;highlighter-rouge&quot;&gt; scope&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这个错误是说，因为 OAuth 的应用没有指定 workflow 范围，所以无法推送带有更新 workflow 的分支。&lt;/p&gt;

&lt;p&gt;虽然我实际上没有对 workflow 做任何更新，但也被拒绝了。所以这个问题必须直接解决，绕不开。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;master:t/walterlv/trigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enumerating&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;objects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Counting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;objects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;compression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compressing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;objects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Writing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;objects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;754&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;754.00&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;KiB/s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Total&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reused&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pack-reused&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Resolving&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deltas:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;objects.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://github.com/dotnet-campus/HandyControl.git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;rejected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t/walterlv/trigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;refusing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;allow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OAuth&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;github&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/workflows/build.yml&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;without&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;failed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'https://github.com/dotnet-campus/HandyControl.git'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;去 GitHub Personal Access Tokens 页面：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/settings/tokens&quot;&gt;Personal Access Tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;生成一个新的 Token。特别注意在生成的时候要勾选 &lt;code class=&quot;highlighter-rouge&quot;&gt;workflow&lt;/code&gt;（如果不确定勾选哪些的话，就全部勾选）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-26-21-30-01.png&quot; alt=&quot;生成新的 Token&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后复制新的 Token：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-26-21-34-28.png&quot; alt=&quot;复制新的 Token&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开凭据管理器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-26-21-35-17.png&quot; alt=&quot;凭据管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Windows 凭据标签下，找到 GitHub 的几个凭据，然后编辑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;git:https://github.com&lt;/li&gt;
  &lt;li&gt;git:https://walterlv@github.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-26-21-43-56.png&quot; alt=&quot;编辑 GitHub 凭据&quot; /&gt;&lt;/p&gt;

&lt;p&gt;把密码改成刚刚复制的那个 Token，然后保存：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-26-21-38-27.png&quot; alt=&quot;粘贴并保存密码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你那里有很多 GitHub 相关的凭据而不确定是哪一个的话，可以考虑全部删掉。这样下次推送的时候就会要求你输入账号密码，输入那个 Token 作为密码即可。&lt;/p&gt;

&lt;p&gt;现在，你就能推送成功了。&lt;/p&gt;
</description>
        <pubDate>Tue, 26 May 2020 13:48:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/github-push-failed-without-workflow-scope.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/github-push-failed-without-workflow-scope.html</guid>
        
        
        <category>github</category>
        
      </item>
    
      <item>
        <title>杂谈 System.Drawing.Common 的跨平台性（关键词：libgdiplus / .NET Core / Mono / Win32 / Linux / ……）</title>
        <description>&lt;p&gt;经过 Mono 团队的不懈努力，原本专属于 Win32 平台的 GDI+ 终于可以跨平台了，不过这中间还有好多的故事和好多的坑。&lt;/p&gt;

&lt;p&gt;本文带你了解 System.Drawing 命名空间的跨平台。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;systemdrawingsystemdrawingcommon-以及-gdi&quot;&gt;System.Drawing、System.Drawing.Common 以及 GDI+&lt;/h2&gt;

&lt;p&gt;在了解本文的后续内容之前，你可能需要先了解一些基本的名词，不然后面极可能看得云里雾里。&lt;/p&gt;

&lt;p&gt;System.Drawing 有两个意思，第一个是 System.Drawing.dll 程序集，第二个是 System.Drawing 命名空间。&lt;/p&gt;

&lt;p&gt;如果进行 .NET Framework 项目的开发，那么对 System.Drawing 一定不陌生，框架自身对位图的处理基本都是用的这套库，很多第三方图像处理库也都基于 System.Drawing 程序集进行二次封装。比如 &lt;a href=&quot;https://github.com/jimbobsquarepants/imageprocessor&quot;&gt;JimBobSquarePants/ImageProcessor&lt;/a&gt; 库实际上就是对 System.Drawing 的封装，&lt;a href=&quot;https://github.com/andrewkirillov/AForge.NET&quot;&gt;AForge.NET&lt;/a&gt; 库作为计算机视觉库也对 System.Drawing 有较大的依赖。&lt;/p&gt;

&lt;p&gt;Mono 是一个诞生以来就为了让 .NET Framework 跨平台的开源项目。开发基于 Mono 运行时的项目时，使用的框架 API 也是兼容 .NET Framework 的，因此也可以在 Mono 中直接依赖 System.Drawing 程序集进行开发。&lt;/p&gt;

&lt;p&gt;System.Drawing 固然强大，但它却只是 Win32 GDI+ 的一层很薄很薄的封装。然而其他平台上没有原生对 GDI+ 的实现，所以跨平台是一个比较棘手的问题（本文后面会说到如何做到跨平台）。&lt;/p&gt;

&lt;p&gt;.NET Core 也是为跨平台而生，不过它走的路线与 Mono 有些不同。它从 API 级别就分离出 .NET Framework 中不跨平台的部分，然后把它们从 .NET 的核心仓库中移除，换成 .NET 的扩展框架（如 WPF / Windows Forms）。那么面对 System.Drawing 部分的 API 时 .NET Core 是怎么做的呢？一开始做了一个兼容库 &lt;a href=&quot;https://www.nuget.org/packages/CoreCompat.System.Drawing/&quot;&gt;CoreCompat.System.Drawing&lt;/a&gt;（仓库在&lt;a href=&quot;https://github.com/CoreCompat/CoreCompat&quot;&gt;这里&lt;/a&gt;和&lt;a href=&quot;https://github.com/CoreCompat/System.Drawing&quot;&gt;这里&lt;/a&gt;）做了一部分的兼容，而后由于 Mono 的努力做出了 GDI+ 在其他平台上的实现（&lt;a href=&quot;https://github.com/mono/libgdiplus&quot;&gt;mono/libgdiplus&lt;/a&gt;），.NET Core 就有幸将 System.Drawing 纳入 .NET Core 中作为一个扩展库存在。而这个库就是 &lt;a href=&quot;https://www.nuget.org/packages/System.Drawing.Common/&quot;&gt;System.Drawing.Common&lt;/a&gt;（仓库在 &lt;a href=&quot;https://github.com/dotnet/runtime/tree/master/src/libraries/System.Drawing.Common&quot;&gt;这里&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;我们小结一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;GDI+ 是 Windows 上的图形设备接口（Graphics Device Interface），用来完成一些和绘制有关的工作，用以解决不同应用程序开发者需要面向具体的硬件绘图造成的兼容负担（类似的有 GTK 的 GDK/Xlib 还有 Mac 设备的 Quartz）。&lt;/li&gt;
  &lt;li&gt;System.Drawing 命名空间里包含了封装 GDI+ 的一层薄薄的封装，System.Drawing.dll 是 .NET Framework 下这层封装的实现，&lt;a href=&quot;https://www.nuget.org/packages/System.Drawing.Common/&quot;&gt;System.Drawing.Common&lt;/a&gt; 库是 .NET Core 下这层封装的实现。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;跨平台的关键-libgdiplus&quot;&gt;跨平台的关键 libgdiplus&lt;/h2&gt;

&lt;p&gt;libgdiplus 是在非 Windows 操作系统上提供 GDI+ 兼容 API 的 Mono 库，而其跨平台图形绘制的大部分关键实现靠的是 &lt;a href=&quot;https://www.cairographics.org/&quot;&gt;Cairo&lt;/a&gt; 库。&lt;/p&gt;

&lt;p&gt;libgdiplus 的开源仓库：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mono/libgdiplus&quot;&gt;mono/libgdiplus: C-based implementation of the GDI+ API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;目前，其几乎就是为 System.Drawing 命名空间下的位图处理作为实现的。System.Drawing 的跨平台的能力几乎完全靠的是 libgdiplus 库。&lt;/p&gt;

&lt;p&gt;安装方法见仓库 README。&lt;/p&gt;

&lt;p&gt;目前 libgdiplus 还有一些没能完全实现的部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;文本
    &lt;ul&gt;
      &lt;li&gt;libgdiplus 目前是自己实现的一套文本引擎，但 GDI+ 提供了 libgdiplus 不支持或不正确支持的许多（很少使用的）选项&lt;/li&gt;
      &lt;li&gt;目前也正考虑使用 pango 引擎来替代自己的实现，可通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;–with-pango&lt;/code&gt; 选项开启 pango 引擎，但没实现的功能更多&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;其他
    &lt;ul&gt;
      &lt;li&gt;还有其他一些没实现的功能&lt;/li&gt;
      &lt;li&gt;可在这里看到尚未实现的功能列表 &lt;a href=&quot;https://github.com/mono/libgdiplus/blob/master/TODO&quot;&gt;libgdiplus/TODO at master · mono/libgdiplus&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Mono 官方欢迎社区广大小伙伴帮忙完成这些任务&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;systemdrawing-各平台目前的支持情况&quot;&gt;System.Drawing 各平台目前的支持情况&lt;/h2&gt;

&lt;p&gt;Mono 和 .NET Core 目前均已完成基于 libgdiplus 的 System.Drawing 命名空间的跨平台。当然，这个跨平台迁移的唯一目的是“兼容”，是为了让现有的基于 System.Drawing 的代码能够跨平台跑起来。仅此而已，不会有任何的性能优化或者设计优化。（想要优化的版本可以参考本文最后推荐的其他图形库）。&lt;/p&gt;

&lt;p&gt;但依然值得注意的是，这个跨平台依然不是完全的跨所有平台：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一是因为前面我们说到的 libgdiplus 尚未完全实现 GDI+ 的所有功能&lt;/li&gt;
  &lt;li&gt;二是因为 Windows 自己的 UWP 平台无法完成 System.Drawing 的实现&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里将其他的基于 .NET / Windows 平台的图形实现放到一起来做对比：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Win32&lt;/th&gt;
      &lt;th&gt;UWP&lt;/th&gt;
      &lt;th&gt;macOS&lt;/th&gt;
      &lt;th&gt;Linux / 其他&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Framework (GDI+)&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Direct2D / Win2D&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mono / .NET Core (libgdiplus)&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Xamarin (CoreGraphics)&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;其他第三方 .NET 库&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;ul&gt;
  &lt;li&gt;.NET Framework 中的实现也就是本文一直在说的重点，即 System.Drawing，即对 GDI+ 那非常薄的封装。&lt;/li&gt;
  &lt;li&gt;Direct2D / Win2D 只能在 Windows 平台使用；如果不使用 UWP 桥，那么 Win2D 也只能局限在 UWP 平台，而且要求系统版本 Windows 8 及以上。&lt;/li&gt;
  &lt;li&gt;Mono / .NET Core 基于 libgdiplus 实现跨平台，但需要注意在 Win32 平台上，它用的也是现成的 GDI+ 实现，而不是 libgdiplus。&lt;/li&gt;
  &lt;li&gt;Xamarin / CoreGraphics 这是使用原生系统组件做的图形实现，仅支持 macOS 平台。&lt;/li&gt;
  &lt;li&gt;其他第三方库因为不强依赖系统组件，所以能做到更好的跨平台特性。（可见本文末尾推荐的图像库。）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;选择-systemdrawingdll-还是选择-systemdrawingcommon&quot;&gt;选择 System.Drawing.dll 还是选择 System.Drawing.Common&lt;/h2&gt;

&lt;h3 id=&quot;问题&quot;&gt;问题&lt;/h3&gt;

&lt;p&gt;回到 System.Drawing 上，现在我们知道应该使用 System.Drawing.dll 还是使用 System.Drawing.Common 库了吗？&lt;/p&gt;

&lt;p&gt;盲猜应该使用 System.Drawing.Common 库吧？因为这个库里面既带了 Windows 平台下的实现（对 GDI+ 做一层很薄的封装），又带了 Linux 和 macOS 下的实现（使用 libgdiplus）。&lt;/p&gt;

&lt;p&gt;然而事情并没有那么简单！我来问几个问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;.NET Framework 里面已经自带了 System.Drawing.dll 了，那么 System.Drawing.Common 包里带的 System.Drawing.Common.dll 是否会与之冲突？
    &lt;ul&gt;
      &lt;li&gt;例如是否会导致同一个类型分属两个不同的程序集导致分别依赖两个不同程序集的不同代码之前无法传递 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing&lt;/code&gt; 命名空间中的参数呢？&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;所有种类的项目都能正常使用 System.Drawing.Common 库吗？
    &lt;ul&gt;
      &lt;li&gt;例如 Unity3D 项目&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;首先来看看问题一。我们新建一个 .NET Framework 的项目，一个 .NET Core 的项目，两者都安装 System.Drawing.Common 包，然后调用一下这个包里面的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\walterlv\test.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;反编译&quot;&gt;反编译&lt;/h3&gt;

&lt;p&gt;会发现，两者都是可以正常运行的。&lt;/p&gt;

&lt;p&gt;将 net48 框架项目下引用的 System.Drawing.Common.dll 反编译来看，可以发现，这是一个空的程序集，里面几乎没有任何实质上的类型。里面所有的类型都通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeForwardedTo&lt;/code&gt; 特性转移到 System.Drawing.dll 程序集了，现在剩下的只是一个垫片。关于 TypeForwarding 可以阅读这篇博客了解：&lt;a href=&quot;https://blog.lindexi.com/post/C-dotnet-TypeForwarding-%E7%9A%84%E7%94%A8%E6%B3%95.html&quot;&gt;C# dotnet TypeForwarding 的用法&lt;/a&gt;，微软也有其他通过此方式做的 NuGet 包，可参见 &lt;a href=&quot;/post/microsoft-dotnet-packages-use-typeforwarded-to-keep-compatibility&quot;&gt;微软官方的 NuGet 包是如何做到同时兼容新旧框架的？ - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-13-44-32.png&quot; alt=&quot;.NET Framework 4.8 下输出的文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-13-34-32.png&quot; alt=&quot;.NET Framework 4.8 中引用的 System.Drawing.Common.dll&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将 netcoreapp3.1 框架项目下引用的 System.Drawing.Common.dll 反编译来看，可以发现，这个程序集里面所有的类型所有的方法实现都是抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-13-42-51.png&quot; alt=&quot;.NET Core 3.1 下输出的文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-13-39-06.png&quot; alt=&quot;.NET Core 中引用的 System.Drawing.Common.dll&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这就有些奇怪了，如果所有的方法都抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt; 那如何才能正常运行呢？&lt;/p&gt;

&lt;p&gt;打开 netcoreapp3.1 输出目录下的 *.deps.json 文件，可以注意到，里面记录了在不同的运行目标下应该使用的真实的 System.Drawing.Common.dll 的文件路径：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;runtimeTargets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;runtimes/unix/lib/netcoreapp3.0/System.Drawing.Common.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;rid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;unix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;assetType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;runtime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;assemblyVersion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4.0.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fileVersion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4.700.19.56404&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;runtimes/win/lib/netcoreapp3.0/System.Drawing.Common.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;rid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;win&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;assetType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;runtime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;assemblyVersion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4.0.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fileVersion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4.700.19.56404&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;去相应的路径下找，可以找到 win 版本的 System.Drawing.Common.dll 和 unix 版本的 System.Drawing.Common.dll。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-13-49-12.png&quot; alt=&quot;win 版本和 unix 版本的 System.Drawing.Common.dll&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些指定的依赖，在发布此程序之后会换成真实的依赖，而不再包含多个不同平台下的 dll 了：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;netcoreapp3.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;win10-x64&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--self-contained&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;拆包&quot;&gt;拆包&lt;/h3&gt;

&lt;p&gt;我们去 &lt;a href=&quot;https://nuget.org&quot;&gt;nuget.org&lt;/a&gt; 上下载下来 &lt;a href=&quot;https://www.nuget.org/packages/System.Drawing.Common/&quot;&gt;System.Drawing.Common&lt;/a&gt; 包拆开来看，会发现这个包有两个很关键的文件夹：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;lib&lt;/li&gt;
  &lt;li&gt;runtimes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，lib 里面包含这些不同的目标框架：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MonoAndroid10&lt;/li&gt;
  &lt;li&gt;MonoTouch10&lt;/li&gt;
  &lt;li&gt;net461&lt;/li&gt;
  &lt;li&gt;netstandard2.0&lt;/li&gt;
  &lt;li&gt;xamarinios10&lt;/li&gt;
  &lt;li&gt;xamarinmac20&lt;/li&gt;
  &lt;li&gt;xamarintvos10&lt;/li&gt;
  &lt;li&gt;xamarinwatchos10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;net461 里包含的 dll 就是前面我们说到的“垫片”，所有的类型都通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeForwardedTo&lt;/code&gt; 转移到 .NET Framework 版本的 System.Drawing.dll。&lt;/p&gt;

&lt;p&gt;netstandard2.0 适用于 .NET Core 框架，里面包含的 dll 就是前面我们说到的所有方法都抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt; 的版本。&lt;/p&gt;

&lt;p&gt;其他所有框架里都是 &lt;em&gt;.&lt;/em&gt; 文件，是个空的文件，仅用来告诉 NuGet 这个包支持这些框架安装，但不引用任何 dll。&lt;/p&gt;

&lt;p&gt;另外，NuGet 包的 runtimes 文件夹里面包含了前面我们说到的 win 和 unix 不同实现版本的 System.Drawing.Common.dll。前面已经给出了反编译的截图，应该足够了解了。你也可以自己去解包，了解里面的目录结构，去反编译看。&lt;/p&gt;

&lt;h3 id=&quot;决定&quot;&gt;决定&lt;/h3&gt;

&lt;p&gt;现在，是时候来决定应该使用 System.Drawing.dll 还是使用 System.Drawing.Common 包了。那么，这里我整理一张表：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;System.Drawing.dll&lt;/th&gt;
      &lt;th&gt;System.Drawing.Common&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Framework 4.6 及以下版本&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Framework 4.6.1 及以上版本&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Core 1.x&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌无法安装包&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Core 2.0 - .NET Core 2.1&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌运行时抛出&lt;br /&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET Core 3.0 及以上版本&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mono / Xamarin&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;✔️表示可以使用，没有问题；❌表示不支持此引用方式。&lt;/p&gt;

&lt;p&gt;另外，这里还要额外说一下 Unity 的支持情况。&lt;/p&gt;

&lt;p&gt;Unity 有两种不同的 C# 脚本后端可选：Mono 和 IL2CPP。然而 Unity 不能原生支持 NuGet 包，而 System.Drawing.Common 包要能够在编译时自动选择正确的 dll 去引用，是需要 3.4 版本以上的 NuGet 程序来支持的。如果不能完全实现此版本 NuGet 的功能，那么编译时是无法将正确的 dll 拷贝到输出目录的。不幸的是，目前流行于 Unity 的第三方 NuGet 管理器不能正确拷贝此包的 dll 到输出目录。&lt;/p&gt;

&lt;p&gt;更具体的，是受以下设置的影响（在编译设置里面）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-14-13-20.png&quot; alt=&quot;Unity 编译设置&quot; /&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;脚本后端&lt;/th&gt;
      &lt;th&gt;Api 兼容级别&lt;/th&gt;
      &lt;th&gt;System.Drawing.dll&lt;/th&gt;
      &lt;th&gt;System.Drawing.Common&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;组合1&lt;/td&gt;
      &lt;td&gt;Mono&lt;/td&gt;
      &lt;td&gt;.NET 4.x&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌相当于没引用&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;组合2&lt;/td&gt;
      &lt;td&gt;Mono&lt;/td&gt;
      &lt;td&gt;.NET Standard 2.0&lt;/td&gt;
      &lt;td&gt;❌相当于没引用&lt;/td&gt;
      &lt;td&gt;❌第三方 NuGet 包管理器会拷贝错误的 dll&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;组合3&lt;/td&gt;
      &lt;td&gt;IL2CPP&lt;/td&gt;
      &lt;td&gt;.NET 4.x&lt;/td&gt;
      &lt;td&gt;❌可在编辑器运行，但打包后会出现异常&lt;/td&gt;
      &lt;td&gt;❌未引用任何库&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;组合4&lt;/td&gt;
      &lt;td&gt;IL2CPP&lt;/td&gt;
      &lt;td&gt;.NET Standard 2.0&lt;/td&gt;
      &lt;td&gt;❌相当于没引用&lt;/td&gt;
      &lt;td&gt;❌第三方 NuGet 包管理器会拷贝错误的 dll&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;是不是很悲惨？只有 Mono / .NET 4.x 这个组合可以正常使用 System.Drawing。当然，如果你愿意用部分手工或自己的脚本/工具来代替第三方 NuGet 包的部分功能，选择出正确的 dll 的话，那么对应的方案也是能行的。&lt;/p&gt;

&lt;p&gt;表中的“❌相当于没引用”指的是引用此 dll 相当于没引用 dll，安装此包相当于没有引用此包：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// .NET 4.x 的 Api 兼容级别报此错误&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;could&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;System.Drawing&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;This&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;been&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forwarded&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neutral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PublicKeyToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b03f5f7f11d50a3a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Consider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// .NET Standard 2.0 的 Api 兼容级别报此错误&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;does&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exist&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;System.Drawing&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;you&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;missing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;IL2CPP 里在编辑器里可以正常使用（当然能正常，因为编辑器又没用 IL2CPP），打包后出现的异常如下（所有的 System.Drawing 方法调用都有异常）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromHbitmap&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHbitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hbitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00000000000000000000000000000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于 Unity 的部分，本文不打算细说。如果你有其他疑问，我就挖个坑，再写一篇来填。&lt;/p&gt;

&lt;h2 id=&quot;不依赖-systemdrawing-的其他免费开源库&quot;&gt;不依赖 System.Drawing 的其他免费开源库&lt;/h2&gt;

&lt;p&gt;如果你当前的开发平台依然无法使用到 System.Drawing 命名空间，那么可以考虑使用另外的一些替代品。这里给出一些推荐：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/SixLabors/ImageSharp&quot;&gt;SixLabors/ImageSharp: A modern, cross-platform, 2D Graphics library for .NET&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mono/SkiaSharp&quot;&gt;mono/SkiaSharp: SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google’s Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你需要的是图像处理，而不需要与 Windows API 有太多关联的话，那么使用这些库会比使用 System.Drawing 带来更优秀的用法、更好的性能以及更现代化的维护方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/issues/21980&quot;&gt;Support Full System.Drawing Functionality on .NET Core · Issue #21980 · dotnet/runtime&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mono/libgdiplus&quot;&gt;mono/libgdiplus: C-based implementation of the GDI+ API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mono/libgdiplus/blob/master/TODO&quot;&gt;libgdiplus/TODO at master · mono/libgdiplus&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mono-project.com/docs/gui/libgdiplus/&quot;&gt;libgdiplus - Mono&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 25 May 2020 01:00:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/system-drawing-common.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/system-drawing-common.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>微软官方的 NuGet 包是如何做到同时兼容新旧框架的？例如 System.ValueTuple 是如何做到在新旧版本的框架都能使用的？</title>
        <description>&lt;p&gt;不知你是否好奇，System.ValueTuple 是新框架（.NET Core 3.0）开始引入的类型，但可以通过 NuGet 包向旧框架提供这些类型的使用。并且，这些包即便安装到本来就有此类型的新框架上也能正常运行而不会出现多处类型定义的问题。&lt;/p&gt;

&lt;p&gt;这些类型是如何做到框架内定义了，包里也定义了，却能像同一个类型一样作为参数和返回值传递？本文带你了解其中的奥秘。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;示例项目&quot;&gt;示例项目&lt;/h2&gt;

&lt;p&gt;首先，我们需要有一个示例项目，用来观察 System.ValueTuple 在框架内和 NuGet 包内的一些行为。&lt;/p&gt;

&lt;p&gt;创建一个 .NET Core 控制台项目。然后我们需要修改两个地方：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Program.cs 文件&lt;/li&gt;
  &lt;li&gt;项目文件（*.csproj）文件&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;欢迎阅读&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;的博客 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net462;net48;netstandard2.0;netcoreapp2.0;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.5.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来，我们的研究都将基于此项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-15-44-37.png&quot; alt=&quot;输出目录&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;研究开始&quot;&gt;研究开始&lt;/h2&gt;

&lt;p&gt;System.ValueTuple 对旧框架的支持体现在三个方面：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;旧框架中也能写出新框架中的这种语法；&lt;/li&gt;
  &lt;li&gt;旧框架中也能正常使用此类型；&lt;/li&gt;
  &lt;li&gt;新框架中此类型不会与包中的类型冲突。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们分别来看看这三个都是如何实现的。&lt;/p&gt;

&lt;h3 id=&quot;语法支持&quot;&gt;语法支持&lt;/h3&gt;

&lt;p&gt;C# 从 7.0 开始支持元组类型的语法，即可以写出这样的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于此新增功能，可以前往这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-7#tuples&quot;&gt;C# 7.0 中的新增功能 - C# 指南 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C# 从 8.0 开始，各种原本需要实现特定接口才能写出的语法，现在也可以不用实现接口了，只要有对应的方法存在即可，比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IDisposable&lt;/li&gt;
  &lt;li&gt;IEnumerable&lt;/li&gt;
  &lt;li&gt;Deconstruct&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，从 C# 5.0 开始引入的 async/await 也是如此，无需实现任何接口，有 GetAwaiter 方法就够了。也是一样的情况，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/what-is-an-awaiter.html&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？ - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;也就是说，只要你的项目使用的 C# 版本在 7.0 以上，就可以使用元组解构这样的语法。即便在 C# 7.0 以下，也能使用 System.ValueTuple，只是不能使用此语法而已。&lt;/p&gt;

&lt;h3 id=&quot;旧框架兼容&quot;&gt;旧框架兼容&lt;/h3&gt;

&lt;p&gt;System.ValueTuple 对旧框架的兼容，单纯的就是通过 NuGet 包引入了这些类型，以及这些类型的实现而已。&lt;/p&gt;

&lt;p&gt;我们在示例项目的 net462 的输出目录下找到 System.ValueTuple.dll 进行反编译可以看出来这一点：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-15-43-00.png&quot; alt=&quot;net462 版本的 System.ValueTuple.dll&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;新框架不冲突&quot;&gt;新框架不冲突&lt;/h3&gt;

&lt;p&gt;我们再去新框架里面看看 System.ValueTuple 的情况。&lt;/p&gt;

&lt;p&gt;例如先看看 net48 目录下的 System.ValueTuple.dll：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-15-45-29.png&quot; alt=&quot;net48 版本的 System.ValueTuple.dll&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以发现，net48 下的 System.ValueTuple 已经全部使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeForwardedTo&lt;/code&gt; 特性转移到了 mscorelib 程序集。&lt;/p&gt;

&lt;p&gt;.NET Core 3.1 版本和 .NET Standard 2.0 版本的输出目录里是没有 System.ValueTuple.dll 的，那么它们的依赖是如何决定的呢？&lt;/p&gt;

&lt;!-- 对于 .NET Standard 2.0 来说，在 *.deps.json 里面记录（其他项已省略）：

```json
&quot;targets&quot;: {
    &quot;.NETStandard,Version=v2.0/&quot;: {
        &quot;Walterlv.Demo.SystemValueTuple/1.0.0&quot;: {
            &quot;dependencies&quot;: {
                &quot;NETStandard.Library&quot;: &quot;2.0.3&quot;,
                &quot;System.ValueTuple&quot;: &quot;4.5.0&quot;
            },
        },
    }
},
```

对于 .NET Core 3.1 也是在 *.deps.json 里面记录（其他项已省略）：

```json
&quot;targets&quot;: {
    &quot;.NETCoreApp,Version=v3.1&quot;: {
        &quot;Walterlv.Demo.SystemValueTuple/1.0.0&quot;: {
            &quot;dependencies&quot;: {
                &quot;System.ValueTuple&quot;: &quot;4.5.0&quot;
            },
            &quot;runtime&quot;: {
                &quot;Walterlv.Demo.SystemValueTuple.dll&quot;: {}
            }
        },
        &quot;System.ValueTuple/4.5.0&quot;: {}
    }
},
```

这些指定的依赖，在发布此程序之后会换成真实的依赖：

```powershell
dotnet publish -c Release -f netcoreapp3.1 -r win10-x64 --self-contained true
```

这是发布后的 dll：

![发布后的 dll](/static/posts/2020-05-24-16-12-29.png)

反编译之后查看，可以发现已经是使用了 `TypeForwardedTo` 特性的 dll 了。

因此，对于新框架来说，是因为使用了 `TypeForwardedTo` 特性使得无论你使用包中的 System.ValueTuple 还是使用框架内的 System.ValueTuple，最终都会对应到框架内的同一个类型。无需担心类型不同的问题。 --&gt;

&lt;p&gt;答案是——不需要依赖！&lt;/p&gt;

&lt;p&gt;我们来拆开 System.ValueTuple 的 NuGet 包看看。可在这里下载：&lt;a href=&quot;https://www.nuget.org/packages/System.ValueTuple/&quot;&gt;NuGet Gallery - System.ValueTuple 4.5.0&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可发现它提供了这些不同框架的支持：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-24-15-57-55.png&quot; alt=&quot;System.ValueTuple 包支持的框架&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;net47 框架使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeForwardedTo&lt;/code&gt; 的垫片&lt;/li&gt;
  &lt;li&gt;net461 / netstandard1.0 / portable-net40+sl4+win8+wp8 框架使用的是完整版本的 System.ValueTuple&lt;/li&gt;
  &lt;li&gt;netcoreapp2.0 / netstandard2.0 / mono 全系列 / xamarin 全系列 / uap 里面是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_._&lt;/code&gt; 占位文件，表示支持此框架且无需任何引用（因为框架已经自带支持）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;原生支持 System.ValueTuple 的框架，其 NuGet 包中的框架内的文件是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_._&lt;/code&gt;，这个文件的出现仅仅是为了能让 zip 里面有一个对应框架的文件夹。而 zip 对空文件夹的支持并不好，所以加一个这样的文件可以避免文件夹消失，造成 NuGet 认为不支持这样的框架。&lt;/p&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;框架（.NET）和语言（C#）现在已是独立升级了，因此在使用旧框架的情况下，也可以使用新语言的特性；&lt;/li&gt;
  &lt;li&gt;旧框架使用的是完整功能的 dll（由 NuGet 包来决定使用正确的 dll）；&lt;/li&gt;
  &lt;li&gt;新框架使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeForwardedTo&lt;/code&gt; 特性作为垫片，重定向类型到新框架中（由 NuGet 包来决定使用正确的 dll）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;额外的，我写过另一个通过此方式获得新旧框架兼容的包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/system-drawing-common&quot;&gt;杂谈 System.Drawing.Common 的跨平台性 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/aspnetcore/issues/744&quot;&gt;What do mean &lt;em&gt;.&lt;/em&gt; files in nuget packages? · Issue #744 · dotnet/aspnetcore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 May 2020 08:27:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/microsoft-dotnet-packages-use-typeforwarded-to-keep-compatibility.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/microsoft-dotnet-packages-use-typeforwarded-to-keep-compatibility.html</guid>
        
        
        <category>dotnet</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>Unity3D (Mono/IL2CPP) 中 P/Invoke 平台调用代码应该如何传委托</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IL2CPP does not support marshaling delegates that point to instance methods to native code.&lt;/code&gt; 你可能平时在 .NET Core / Framework 的代码中写得很正常的托管代码的委托调用，在 Unity3D 中变得不可行。&lt;/p&gt;

&lt;p&gt;本文举个例子，并且将其改正。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;举例查找所有可见窗口&quot;&gt;举例：查找所有可见窗口&lt;/h2&gt;

&lt;p&gt;本文的例子会使用到 NuGet 包 &lt;a href=&quot;https://www.nuget.org/packages/Lsj.Util.Win32/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Lsj.Util.Win32&lt;/code&gt;&lt;/a&gt;，这是个非常棒的 Win32 调用的 API 包装，可以免去大量自己可能写不对的 &lt;code class=&quot;highlighter-rouge&quot;&gt;[DllImport]&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;引入命名空间：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.BaseTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后查找所有的可见窗口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindVisibleWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsWindowVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;mono--il2cpp&quot;&gt;Mono / IL2CPP&lt;/h2&gt;

&lt;p&gt;Unity 编译的时候可以选择脚本后端是 Mono 还是 IL2CPP。不幸的是，没有 .NET Core 或者未来的 .NET 5/6，因此很多 .NET Core 的特性不能用。&lt;/p&gt;

&lt;p&gt;关于脚本后端的选择，可以参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/unity-starter-reference-dlls-and-add-nuget-package-for-unity-csharp-projects.html&quot;&gt;Unity3D 入门：为 Unity 的 C# 项目添加 dll 引用或安装 NuGet 包 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在编译时不会有什么问题，但是在运行时会发生异常（如果你去捕捉，或者用 VS 调试就可以看到）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IL2CPP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;does&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;support&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marshaling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delegates&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;native&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;we&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;re&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attempting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marshal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Win32WindowExtensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c__DisplayClass0_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindVisibleWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g__OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lsj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lsj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WNDENUMPROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpEnumFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lsj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Win32WindowExtensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindVisibleWindows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;“IL2CPP 不支持封送实例方法到本机代码”。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;修正代码&quot;&gt;修正代码&lt;/h2&gt;

&lt;p&gt;Mono/IL2CPP 要求封送到本机的代码必须是静态方法，且必须标 &lt;code class=&quot;highlighter-rouge&quot;&gt;MonoPInvokeCallback&lt;/code&gt; 特性。&lt;/p&gt;

&lt;p&gt;因此，我们不得不把上面的代码改成这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.BaseTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowsEnumerator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_currentList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindVisibleWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_currentList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_currentList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MonoPInvokeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WNDENUMPROC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsWindowVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_currentList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然上述代码不是线程安全的。所以如果你希望在多线程环境下使用，请自行修改为线程安全的版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/objcruntime.monopinvokecallbackattribute&quot;&gt;MonoPInvokeCallbackAttribute Class (ObjCRuntime) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 May 2020 02:21:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity3d-marshal-callback-must-be-static.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity3d-marshal-callback-must-be-static.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows/Linux 系统中获取端口被哪个应用程序占用</title>
        <description>&lt;p&gt;管理服务程序的时候，可能会查询某个端口当前被哪个进程占用。不仅能找出有问题的进程将其处理掉，也可以用来辅助检查某个程序是否开启了服务并在监听端口。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-系统&quot;&gt;Windows 系统&lt;/h2&gt;

&lt;p&gt;Windows 系统上可以使用 PowerShell 命令来查询占用某个端口的程序。&lt;/p&gt;

&lt;p&gt;比如，我们需要查询 5000 端口被占用的进程是谁，可以在 PowerShell 中输入命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Get-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-NetTCPConnection&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-LocalPort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OwningProcess&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-11-15-04-19.png&quot; alt=&quot;查询占用某端口的进程&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;linux-系统&quot;&gt;Linux 系统&lt;/h2&gt;

&lt;p&gt;在终端中输入命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;lsof&lt;/code&gt; 可以查询占用某个端口的进程。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lsof&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;端口号&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如，我们需要查询 5000 端口被占用的进程是谁，可以在中断中输入命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv@localhost:~# lsof &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;:5000
COMMAND        PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
dotnet_serve   731 root    3u  IPv6  12890      0t0  TCP &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;:5000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者使用 netstat 查询。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;netstat &lt;span class=&quot;nt&quot;&gt;-tunpl&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;端口号
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;举例：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv@localhost:~# netstat &lt;span class=&quot;nt&quot;&gt;-tunpl&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;35412
tcp6   0   0 :::5000     :::&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;             731/dotnet_serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 23 May 2020 12:21:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-port-owning-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-port-owning-process.html</guid>
        
        
        <category>windows</category>
        
        <category>linux</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>如何在 Unity3D 场景中显示帧率（FPS）</title>
        <description>&lt;p&gt;本文介绍如何在 Unity3D 场景中显示帧率。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;插入-uitext&quot;&gt;插入 UI：Text&lt;/h2&gt;

&lt;p&gt;做 FPS 帧率显示需要用到 UI 对象 Text，因此你需要有一个 Canvas。关于在 Unity3D 中插入 UI 对象的方法可见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-add-game-ui&quot;&gt;Unity3D 入门：如何为游戏添加 UI - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当添加了 Canvas 后，再在 Canvas 里添加 Text：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-15-16-44.png&quot; alt=&quot;添加 Text&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;设置文本的属性和布局&quot;&gt;设置文本的属性和布局&lt;/h2&gt;

&lt;p&gt;选中文本对象，在 Inspector 窗格中有很多需要设置的属性。如下图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-15-26-54.png&quot; alt=&quot;设置属性和布局&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;锚点对齐&quot;&gt;锚点对齐&lt;/h3&gt;

&lt;p&gt;上图中，我把点击对齐格子的弹出框放到了场景空间中（截图而已，实际不能放），不然会遮挡窗口中的其他属性。&lt;/p&gt;

&lt;p&gt;这里在水平和垂直方向上都分别可以设置 4 种对齐方式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;左/上 对齐&lt;/li&gt;
  &lt;li&gt;居中对齐&lt;/li&gt;
  &lt;li&gt;右/下 对齐&lt;/li&gt;
  &lt;li&gt;拉伸对齐&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;默认是水平垂直居中，于是 UI 对象会以场景的中心为参考点布局。如果你强行把文本对象拉到左上角，那么你会失去分辨率自适应的特性。&lt;/p&gt;

&lt;p&gt;由于本文期望 FPS 显示到左上角，所以我把锚点设置成左上角。&lt;/p&gt;

&lt;h3 id=&quot;相对位置大小&quot;&gt;相对位置，大小&lt;/h3&gt;

&lt;p&gt;接着，使用鼠标拖拽文本到合适的位置。也可以直接在 Inspector 窗口中设置 PosX 和 PosY 属性，这样更精确。&lt;/p&gt;

&lt;p&gt;也许你注意到还有一个 PosZ 属性可以设置。如果你在 2D 视图中，那么你会发现设置这个属性是“无效”的，但只要切回 3D 视图，你就能发现还是有深度变化的。不过，在设置 Canvas 的 Render Mode 属性之前（保持默认值），这个设置依然还是没有意义，因为默认情况下 UI 在最终显示的时候是始终保持 2D 视图的。&lt;/p&gt;

&lt;p&gt;可以拖拉鼠标调整文本框的大小，也可以设置 Width 和 Height 属性。&lt;/p&gt;

&lt;h3 id=&quot;设置文本的文字内容字体大小和颜色&quot;&gt;设置文本的文字内容、字体大小和颜色&lt;/h3&gt;

&lt;p&gt;在下面的 Text 组件里面，你还可以设置通常本文应该有的属性，调整到你觉得合适的值就好。&lt;/p&gt;

&lt;h2 id=&quot;添加帧率计算脚本&quot;&gt;添加帧率计算脚本&lt;/h2&gt;

&lt;p&gt;接下来我们开始添加帧率计算脚本。&lt;/p&gt;

&lt;h3 id=&quot;创建脚本&quot;&gt;创建脚本&lt;/h3&gt;

&lt;p&gt;在 Inspector 窗口中添加 AddComponent 添加组件，选择新脚本，取个名字。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-15-39-55.png&quot; alt=&quot;添加脚本&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;设计脚本属性&quot;&gt;设计脚本属性&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine.UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FpsUpdater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在脚本中公开一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;fpsText&lt;/code&gt;，用来在 Inspector 窗口中制定要更新的文本 UI。&lt;/p&gt;

&lt;p&gt;然后，将文本对象拖到脚本的 Fps Text 属性上，这样我们就可以在脚本中直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;fpsText&lt;/code&gt; 字段拿到要更新文本的 Text 对象了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-15-45-46.png&quot; alt=&quot;设置 Fps Text 属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，直接用 &lt;code class=&quot;highlighter-rouge&quot;&gt;gameObject&lt;/code&gt; 也是可以的，不过需要自己再做类型转换。&lt;/p&gt;

&lt;h3 id=&quot;编写代码&quot;&gt;编写代码&lt;/h3&gt;

&lt;h4 id=&quot;最简单的&quot;&gt;最简单的&lt;/h4&gt;

&lt;p&gt;最简单的获取 FPS 的方式是直接用 1 除以当前帧所经历的时间。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FPS: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而当你实际使用的时候你就会觉得——嗯……眼睛会瞎的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-flash-float-fps.gif&quot; alt=&quot;闪烁的 FPS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你也有可能发现文字一时出现一时消失，那可能是因为你文本框的宽度设小了。于是当小数点后位数多了一些之后，显示不下去，文字就会消失。&lt;/p&gt;

&lt;p&gt;至少，取个整还是需要的吧，谁愿意看小数帧数呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FPS: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mathf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ceil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;更稳定的&quot;&gt;更稳定的&lt;/h4&gt;

&lt;p&gt;加了取整还是变化很快，看不清。那么可以如何更稳定呢？&lt;/p&gt;

&lt;p&gt;可以考虑累加多帧再一次性更新。比如这里 60 帧更新一次：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine.UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FpsUpdater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FPS: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mathf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ceil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-steady-fps.gif&quot; alt=&quot;稳定的帧率指示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或者考虑 0.5 秒更新一次：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fpsText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FPS: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mathf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ceil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-steady-fps-in-seconds.gif&quot; alt=&quot;每秒刷新一次的更稳定的帧率指示&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;更多脚本&quot;&gt;更多脚本&lt;/h4&gt;

&lt;p&gt;更多 FPS 帧数显示的脚本，可以从本文末尾的参考资料处找到。有很多不同需求的（比如帧率过低飘红的设定，比如要精确）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.unity.com/questions/1189486/how-to-see-fps-frames-per-second.html&quot;&gt;how to see fps? (frames per second) - Unity Answers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.unity.com/questions/46745/how-do-i-find-the-frames-per-second-of-my-game.html&quot;&gt;How do I find the frames per second of my game? - Unity Answers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wiki.unity3d.com/index.php?title=FramesPerSecond&quot;&gt;FramesPerSecond - Unify Community Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.unity.com/questions/64331/accurate-frames-per-second-count.html&quot;&gt;Accurate Frames Per Second Count - Unity Answers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 08:37:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-show-fps.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-show-fps.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：如何为游戏添加 UI</title>
        <description>&lt;p&gt;早期的 Unity3D 做 UI 并不容易，以至于大家习惯于使用 NGUI 插件来开发。后来 NGUI 的开发者加入开发了 Unity UI，现在就有了一套更好用的 Unity UI 可用了。&lt;/p&gt;

&lt;p&gt;本文简单介绍如何添加 UI 作为入门，不会深入介绍各种 UI 和细节。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加-canvas&quot;&gt;添加 Canvas&lt;/h2&gt;

&lt;p&gt;Unity UI 都需要放到 Canvas 上才能工作。你可以像如下图这样插入一个 Canvas。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-14-46-33.png&quot; alt=&quot;插入 Canvas&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，你也可以插入其他的 UI 对象，不过最终 Unity 编辑器都会帮你插入一个 Canvas，然后把你插入的对象放到这个 Canvas 里面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-14-50-18.png&quot; alt=&quot;插入的 Canvas&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;eventsystem&quot;&gt;EventSystem&lt;/h2&gt;

&lt;p&gt;当你开始向场景中插入 Unity 后，同时也会插入一个 EventSystem 游戏对象。EventSystem 的作用是接收系统中的输入事件，以便 UI 元素能够接受到这些事件处理用户的输入。&lt;/p&gt;

&lt;h2 id=&quot;canvas-的属性&quot;&gt;Canvas 的属性&lt;/h2&gt;

&lt;p&gt;在 Inspector 窗口中，表示 Canvas 在场景中位置的对象是 RectTransform 对象了，不再是 Transform 对象。这是定位 UI 的坐标而设计的新的类型。&lt;/p&gt;

&lt;p&gt;你无法修改 Canvas 的 RectTransform 对象的任何属性，这样 Unity 才可以让这个 Canvas 能根据分辨率自适应。&lt;/p&gt;

&lt;h2 id=&quot;其他-ui&quot;&gt;其他 UI&lt;/h2&gt;

&lt;p&gt;关于 Unity UI 的其他细节，我将在单独的博客中说明。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-show-fps&quot;&gt;如何在 Unity3D 场景中显示帧率（FPS） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;切换成-2d-视图&quot;&gt;切换成 2D 视图&lt;/h2&gt;

&lt;p&gt;在开发（2D）UI 的时候，建议将场景视图切换成 2D，这样比较容易做布局。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-15-19-37.png&quot; alt=&quot;切换成 2D&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，如果你不像我这样能看得到整个 Canvas 的话，可以考虑调整下视角或者直接双击 &lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;关于操作视角和快捷键，可以参考我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-unity-editor-shortcut-keys.html&quot;&gt;Unity3D 入门：Unity Editor 编辑器常用快捷键 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 07:21:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-add-game-ui.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-add-game-ui.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：为 Unity 的 C# 项目添加 dll 引用或安装 NuGet 包</title>
        <description>&lt;p&gt;因为 Visual Studio 有强大的包管理器插件，所以即便是不熟悉 NuGet 命令的小伙伴也能轻松安装和管理 NuGet 包。不过，对 Unity C# 项目来说，你并不能直接引用 dll，也不能直接使用自带的 NuGet 包管理器完成 NuGet 包安装。&lt;/p&gt;

&lt;p&gt;本文介绍原因和真正的引用方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;对于传统 .NET/C# 的开发者来说，在解决方案中管理 NuGet 包，在 C# 项目中引用 dll 或 NuGet 包是家常便饭。但在 Unity 项目里面，你可能要改变这一观念——因为 Unity 项目里面实际上并不存在 sln 和 csproj 文件。&lt;/p&gt;

&lt;p&gt;简单了解项目根目录的 sln 文件和 csproj 文件将有助于你理解为什么要像本文一样引用 dll 和安装 NuGet 包，因此如果你不了解，建议先阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-the-sln-and-csproj-files.html&quot;&gt;Unity3D 入门：使用 Visual Studio 开发 Unity C# 脚本，说说根目录的那些 sln 和 csproj 文件 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如何引用-dll-或者安装-nuget-包&quot;&gt;如何引用 dll 或者安装 NuGet 包&lt;/h2&gt;

&lt;p&gt;在 Unity 中，是给 C# 脚本引用 dll 或者安装 NuGet 包，而不能给 C# 项目做 dll 引用。&lt;/p&gt;

&lt;p&gt;Unity 中引用 dll 有两种官方途径：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\csc.rsp&lt;/code&gt; 文件，用于指定引用 .NET 运行时的 dll&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\Plugins&lt;/code&gt; 文件夹，用于指定引用单独的 dll 文件&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，这两个能否正常使用，以及扔到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Plugins&lt;/code&gt; 文件夹中的 dll 应该是什么平台，取决于 Unity 项目的配置。&lt;/p&gt;

&lt;p&gt;当然，引用 NuGet 包的话更推荐非官方的方法，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/third-party-unity-nuget-management.html&quot;&gt;如何管理 Unity 项目中的 NuGet 包？使用第三方 NuGet 包管理器——NuGetForUnity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;配置运行时和-api-兼容性级别&quot;&gt;配置运行时和 API 兼容性级别&lt;/h3&gt;

&lt;p&gt;在 Unity 编辑器中，打开“Edit”-&amp;gt;“Project Settings…”-&amp;gt;“Player”-&amp;gt;“Other Settings”-&amp;gt;“Configuration”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-20-50-54.png&quot; alt=&quot;项目设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里我们关心脚本后端（相当于运行时部分），以及 API 兼容性级别。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-20-52-55.png&quot; alt=&quot;脚本后端和 API 兼容性级别&quot; /&gt;&lt;/p&gt;

&lt;p&gt;脚本后端设置的是脚本如何运行，而 API 兼容性级别设置的是编译时应该使用哪一套 API。&lt;/p&gt;

&lt;p&gt;选 Mono 那么使用 Mono 虚拟机运行，选 IL2CPP 那么会编译 IL 到静态的 cpp 文件不依靠 Mono VM。&lt;/p&gt;

&lt;p&gt;如果选 .NET 4.x 那么你能引用到 .NET Framework 4.x 子集的 API，如果是 .NET Standard 那么能引用到 .NET Standard 程序集。&lt;/p&gt;

&lt;p&gt;你可以通过 &lt;a href=&quot;https://zhuanlan.zhihu.com/p/19972689&quot;&gt;Unity将来时：IL2CPP是什么？ - 知乎&lt;/a&gt; 简单了解 IL2CPP 是什么。&lt;/p&gt;

&lt;h3 id=&quot;mcsrsp&quot;&gt;mcs.rsp&lt;/h3&gt;

&lt;p&gt;如果你的 API 兼容性级别是 .NET Standard 2.0，那么你不应该使用此 mcs.rsp 文件。因为当你选择 .NET Standard 2.0 的 API 级别后，.NET Standard 2.0 中的所有依赖就全部引入了，如果还缺，那也不会在 .NET Standard 2.0 里面，你应该考虑后面“Plugins”的引用方式。&lt;/p&gt;

&lt;p&gt;接下来，我们说说当你使用 .NET 4.x 的 API 级别时，应该如何使用 mcs.rsp 来引用 dll。&lt;/p&gt;

&lt;p&gt;例如对于下图（图来自微软官方文档），希望使用 .NET 4.x 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; 类型。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-21-18-05.png&quot; alt=&quot;缺少 HttpClient 类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;向 Unity 项目的 Assets 文件夹新建一个 mcs.rsp 文件，里面添加以下内容：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dll&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这表示此 Unity 项目中的 C# 脚本引用 .NET Framework 中的 System.Net.Http 程序集。之后，你就能使用诸如 &lt;code class=&quot;highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; 这些类型。&lt;/p&gt;

&lt;p&gt;你也可以使用同样的方式引用其他的 dll，每行一个。&lt;/p&gt;

&lt;p&gt;默认情况下，Unity 会帮我们引用这些 .NET 4.x 的程序集：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;mscorlib.dll&lt;/li&gt;
  &lt;li&gt;System.dll&lt;/li&gt;
  &lt;li&gt;System.Core.dll&lt;/li&gt;
  &lt;li&gt;System.Runtime.Serialization.dll&lt;/li&gt;
  &lt;li&gt;System.Xml.dll&lt;/li&gt;
  &lt;li&gt;System.Xml.Linq.dll&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此，你不需要手工将它们加入到 mcs.rsp 文件中。&lt;/p&gt;

&lt;h3 id=&quot;plugins&quot;&gt;Plugins&lt;/h3&gt;

&lt;p&gt;对于 .NET 4.x 或者 .NET Standard 2.0 中不带的类型，那么你应该使用 Plugins 文件夹来解决。&lt;/p&gt;

&lt;p&gt;在 Assets 文件夹中新建 Plugins 文件夹，然后将你希望引用的 dll 丢进去就完成了。&lt;/p&gt;

&lt;h4 id=&quot;引用-dll&quot;&gt;引用 dll&lt;/h4&gt;

&lt;p&gt;因此，如果你已经拥有了 dll 了，那么直接往 Plugins 文件夹扔就好了。但是你需要注意，扔进去的 dll 需要兼容目标运行时（如 Mono 虚拟机）以及目标平台（例如 iOS）。&lt;/p&gt;

&lt;h4 id=&quot;安装-nuget-包&quot;&gt;安装 NuGet 包&lt;/h4&gt;

&lt;p&gt;原生 Unity 项目不能直接安装 NuGet 包，但可以通过第三方插件实现。&lt;/p&gt;

&lt;h5 id=&quot;原生&quot;&gt;原生&lt;/h5&gt;

&lt;p&gt;原生 Unity 项目并不能直接安装 NuGet 包，所以实际上对于 NuGet 包的引用是通过把包里的 dll 丢到 Plugins 文件夹来实现的。&lt;/p&gt;

&lt;p&gt;既然如此，那就看如何丢进去更有效率了。&lt;/p&gt;

&lt;p&gt;微软&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/cross-platform/unity-scripting-upgrade&quot;&gt;官方文档&lt;/a&gt;的方法是直接从 &lt;a href=&quot;https://nuget.org&quot;&gt;nuget.org&lt;/a&gt; 上直接把包下载下来，解压，然后将对应平台的 dll 从 lib 文件夹中取出来（例如 API 兼容性级别是 .NET Standard 2.0 的项目，请拷贝 lib/netstandard2.0 中的 dll 出来）。&lt;/p&gt;

&lt;p&gt;因为 Unity 编辑器生成了 sln 和 csproj，所以在 Visual Studio 里安装也是可以的，不过这里的安装并不会真实生效，而是我们在 Unity 项目的根目录的 Packages 文件夹中能找到我们安装的 NuGet 包，也是从对应的文件夹中取出来 dll 丢到 Plugins 文件夹中。&lt;/p&gt;

&lt;h5 id=&quot;第三方&quot;&gt;第三方&lt;/h5&gt;

&lt;p&gt;更推荐非官方的方法，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/third-party-unity-nuget-management.html&quot;&gt;如何管理 Unity 项目中的 NuGet 包？使用第三方 NuGet 包管理器——NuGetForUnity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;特别注意反射需要额外支持&quot;&gt;特别注意：反射需要额外支持&lt;/h3&gt;

&lt;p&gt;如果你前面的脚本后端（Script Backend）选择了 IL2CPP，那么小心 dll 的元数据会丢失，依赖于反射的功能也将崩溃。例如大量依赖于反射的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Newtonsoft.Json&lt;/code&gt; 库就会在此情况下无法正常工作。&lt;/p&gt;

&lt;p&gt;如果你需要用到反射，或者你用到的某库中需要依赖反射功能，那么请在 Assets 文件夹中添加 link.xml 文件，内容如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;linker&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;assembly&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fullname=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Core&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fullname=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Linq.Expressions.Interpreter.LightLambda&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;preserve=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/assembly&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/linker&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这将确保 Unity 的字节码剥离过程在导出到 IL2CPP 平台时不会删除必要的数据。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.unity3d.com/Manual/dotnetProfileAssemblies.html?_ga=2.153567932.542818802.1587977026-543747318.1585549821&quot;&gt;Unity - Manual: Referencing additional class library assemblies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 06:38:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-reference-dlls-and-add-nuget-package-for-unity-csharp-projects.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-reference-dlls-and-add-nuget-package-for-unity-csharp-projects.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Win32/C# 应用使用 PrintWindow 对窗口截图（PrintWindow）</title>
        <description>&lt;p&gt;相比于 Windows 2000 引入到 GDI+ 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitBlt&lt;/code&gt; 方法截取窗口图片，Windows XP 时也引入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrintWindow&lt;/code&gt; 方法来专门截取窗口，截取的原理也不同。&lt;/p&gt;

&lt;p&gt;微软 Office 系列里的截取窗口，用的就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrintWindow&lt;/code&gt; 方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;开始截图&quot;&gt;开始截图&lt;/h2&gt;

&lt;p&gt;相比于使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitBlt&lt;/code&gt; 方法，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrintWindow&lt;/code&gt; 截取窗口的代码少得多。&lt;/p&gt;

&lt;p&gt;你需要引用如下命名空间：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Drawing.Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Drawing.Graphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CaptureWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memoryGraphics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memoryGraphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
           &lt;span class=&quot;nf&quot;&gt;PrintWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;memoryGraphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReleaseHdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

           &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemoryStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MemoryStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Png&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeekOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原理效果和问题&quot;&gt;原理、效果和问题&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrintWindow&lt;/code&gt; 来截图时，目标窗口会收到一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_PRINT&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_PRINTCLIENT&lt;/code&gt; 消息以完成一次绘图。并且，此过程是同步进行的，如果目标窗口在处理消息时没有返回，那么这里的调用将一直挂起。&lt;/p&gt;

&lt;p&gt;使用此方法截图时，DWM 绘制的窗口部分在真实窗口中和实际截出来的会不一样，是关掉了 Aero 效果时的窗口样式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-14-28-54.png&quot; alt=&quot;关掉了 Aero 样式的截图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，还有可能把目标窗口截挂：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-14-29-28.png&quot; alt=&quot;截到没有目标窗口了&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;更多截窗口方法&quot;&gt;更多截窗口方法&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-and-system-drawing-capture-window-to-bitmap&quot;&gt;Win32/C# 应用使用 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/pure-win32-capture-window-to-bitmap&quot;&gt;Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;（本文）&lt;a href=&quot;/post/win32-capture-window-using-print-window&quot;&gt;Win32/C# 应用使用 PrintWindow 对窗口截图（PrintWindow） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-printwindow&quot;&gt;PrintWindow function (winuser.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/272066252&quot;&gt;office的截屏是用的什么技术？ - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 06:32:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win32-capture-window-using-print-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win32-capture-window-using-print-window.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt）</title>
        <description>&lt;p&gt;在 Windows 上有 GDI+ 来操作位图，不止能完成很多的位图操作，还提供了与 Win32 窗口的互操作，可以截到 Win32 窗口的图片。&lt;/p&gt;

&lt;p&gt;如果你希望对窗口截图，那么可使用本文提供的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;没有依赖&quot;&gt;没有依赖&lt;/h2&gt;

&lt;p&gt;本文对窗口的截图几乎不需要任何额外的依赖（当然，都 GDI 了，Windows 系统还是要的）。&lt;/p&gt;

&lt;p&gt;不过，你可以考虑使用 &lt;a href=&quot;https://www.nuget.org/packages/Lsj.Util.Win32/&quot;&gt;Lsj.Util.Win32&lt;/a&gt; 来简化代码，所以如果不介意的话也推荐安装，避免手工写一大堆的 P/Invoke。如果打算自己写 P/Invoke 又不熟的话，你可以参考 &lt;a href=&quot;/post/pinvoke-net-visual-studio-extension&quot;&gt;使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果你的项目可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing.Bitmap&lt;/code&gt; 类的话，那更推荐直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bitmap&lt;/code&gt;，那样更简单。请参考 &lt;a href=&quot;/post/pure-win32-capture-window-to-bitmap&quot;&gt;Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;开始截图&quot;&gt;开始截图&lt;/h2&gt;

&lt;p&gt;如果你使用了 Lsj.Util.Win32 库，那么需要引用一些命名空间：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.BaseTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.Enums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.Structs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个命名空间中已经带了很多我们需要用到的 Win32 互操作需要用到的数据结构，所以本文代码中只会列出库中暂时没有的（不然代码太多了）。&lt;/p&gt;

&lt;p&gt;代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CaptureWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建兼容内存 DC。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindowDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateCompatibleDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建兼容位图 DC。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateCompatibleBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 关联兼容位图和兼容内存，不这么做，下面的像素位块（bit_block）转换不会生效到 hBitmap。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldHBitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 注：使用 GDI+ 截取“使用硬件加速过的”应用时，截取到的部分是全黑的。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BitBlt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RasterCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SRCCOPY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 保存图片。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetImageFromHBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLastError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Win32Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 回收资源。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldHBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReleaseDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetImageFromHBitmap&lt;/code&gt; 方法的实现就比较麻烦了——我们需要手工写图片文件的文件头！&lt;/p&gt;

&lt;p&gt;分成三个部分写入：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;BMP 位图文件头&lt;/li&gt;
  &lt;li&gt;BMP 信息&lt;/li&gt;
  &lt;li&gt;位图数据&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;实现如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetImageFromHBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HDC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HBITMAP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPINFOHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapInfoHeader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BITMAPINFOHEADER&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPINFOHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biPlanes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biBitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biCompression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Compression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BI_PNG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biSizeImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biXPelsPerMeter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biYPelsPerMeter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biClrUsed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biClrImportant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fixed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpvBits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpvBitsOnData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpvBits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPINFOHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;got&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDIBits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpvBitsOnData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BITMAPINFO&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bmiColors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RGBQUAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bmiHeader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapInfoHeader&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DIBColorTableIdentifiers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DIB_RGB_COLORS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileHeader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bfOffBits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPINFOHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bfSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bfType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x4D42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// BM&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;GetBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;GetBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bitmapInfoHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AllocHGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructureToPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FreeHGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BITMAPFILEHEADER&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bfSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bfReserved1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bfReserved2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bfOffBits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里代代码不涉及到格式转换，因此你只能生成 BMP 格式。&lt;/p&gt;

&lt;h2 id=&quot;更多截窗口方法&quot;&gt;更多截窗口方法&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-and-system-drawing-capture-window-to-bitmap&quot;&gt;Win32/C# 应用使用 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;（本文）&lt;a href=&quot;/post/pure-win32-capture-window-to-bitmap&quot;&gt;Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-capture-window-using-print-window&quot;&gt;Win32/C# 应用使用 PrintWindow 对窗口截图（PrintWindow） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/gdi/capturing-an-image&quot;&gt;Capturing an Image - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 05:52:28 +0000</pubDate>
        <link>https://blog.walterlv.com/post/pure-win32-capture-window-to-bitmap.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/pure-win32-capture-window-to-bitmap.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Win32/C# 应用使用 GDI+ 对窗口截图（BitBlt）</title>
        <description>&lt;p&gt;在 Windows 上有 GDI+ 来操作位图，不止能完成很多的位图操作，还提供了与 Win32 窗口的互操作，可以截到 Win32 窗口的图片。&lt;/p&gt;

&lt;p&gt;如果你希望对窗口截图，那么可使用本文提供的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;依赖或者没有依赖&quot;&gt;依赖，或者没有依赖&lt;/h2&gt;

&lt;p&gt;在本文的代码中，你可以考虑引用以下这些库来简化代码。&lt;/p&gt;

&lt;p&gt;对于 .NET Core：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.Drawing.Common&quot;&gt;System.Drawing.Common&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Lsj.Util.Win32/&quot;&gt;Lsj.Util.Win32&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于 .NET Framework / Mono：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;System.Drawing.dll&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Lsj.Util.Win32/&quot;&gt;Lsj.Util.Win32&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上所有库都是可选的。&lt;/p&gt;

&lt;p&gt;如果你打算不引用 Lsj.Util.Win32，那么下面代码中涉及到的 Win32 API 调用你需要自己写 P/Invoke。如果你不熟悉 P/Invoke 的写法，你可以参考 &lt;a href=&quot;/post/pinvoke-net-visual-studio-extension&quot;&gt;使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果你不打算引用 System.Drawing.Common，那么可以考虑使用裸的 GDI+ 来完成，可以参考 &lt;a href=&quot;/post/pure-win32-capture-window-to-bitmap&quot;&gt;Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;开始截图&quot;&gt;开始截图&lt;/h2&gt;

&lt;p&gt;如果你使用了 Lsj.Util.Win32 库，那么需要引用一些命名空间：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.BaseTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.Enums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lsj.Util.Win32.Structs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CaptureWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建兼容内存 DC。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindowDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateCompatibleDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建兼容位图 DC。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateCompatibleBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 关联兼容位图和兼容内存，不这么做，下面的像素位块（bit_block）转换不会生效到 hBitmap。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldHBitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 注：使用 GDI+ 截取“使用硬件加速过的”应用时，截取到的部分是全黑的。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BitBlt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RasterCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SRCCOPY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 保存图片。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHbitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MemoryStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Png&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeekOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLastError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Win32Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 回收资源。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldHBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Gdi32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;User32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReleaseDC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wdc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;示例代码只是单纯返回 PNG 格式的位图数据。你还可以按你的需要改造成其他数据。&lt;/p&gt;

&lt;h2 id=&quot;更多截窗口方法&quot;&gt;更多截窗口方法&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;（本文）&lt;a href=&quot;/post/win32-and-system-drawing-capture-window-to-bitmap&quot;&gt;Win32/C# 应用使用 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/pure-win32-capture-window-to-bitmap&quot;&gt;Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图（BitBlt） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-capture-window-using-print-window&quot;&gt;Win32/C# 应用使用 PrintWindow 对窗口截图（PrintWindow） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/gdi/capturing-an-image&quot;&gt;Capturing an Image - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 05:52:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win32-and-system-drawing-capture-window-to-bitmap.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win32-and-system-drawing-capture-window-to-bitmap.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>提高使用 Visual Studio 开发效率的键盘快捷键</title>
        <description>&lt;p&gt;Visual Studio 的功能可谓真是丰富，再配合各种各样神奇强大的插件，Visual Studio 作为太阳系最强大的 IDE 名副其实。&lt;/p&gt;

&lt;p&gt;如果你能充分利用起 Visual Studio 启用这些功能的快捷键，那么效率也会很高。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;建议记住&quot;&gt;建议记住&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;功能&lt;/th&gt;
      &lt;th&gt;快捷键&lt;/th&gt;
      &lt;th&gt;建议修改成&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;重构&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Enter&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;转到所有&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + ,&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + N&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;重命名&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F2&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;打开智能感知列表&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + J&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + 右&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;注释&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + C&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;取消注释&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + U&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;保存全部文档&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, S&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;折叠成大纲&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + O&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;展开所有大纲&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + P&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;加入书签&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + K&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;上一书签&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + P&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;下一书签&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + N&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;切换自动换行&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Z&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;万能重构&quot;&gt;万能重构&lt;/h2&gt;

&lt;p&gt;你可以不记住本文的其他任何快捷键，但这个你一定要记住，那就是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-19-03-27.png&quot; alt=&quot;Ctrl + .&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，因为中文输入法会占用这个快捷键，所以我更喜欢将这个快捷键修改一下，改成：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-19-05-30.png&quot; alt=&quot;Alt + Enter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;修改方法可以参见：&lt;a href=&quot;/post/customizing-keyboard-shortcuts-in-visual-studio&quot;&gt;如何快速自定义 Visual Studio 中部分功能的快捷键&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;它的功能是“快速操作和重构”。你几乎可以在任何代码上使用这个快捷键来快速修改你的代码。&lt;/p&gt;

&lt;p&gt;比如修改命名空间：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-19-07-26.png&quot; alt=&quot;修改命名空间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如提取常量或变量：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-19-11-16.png&quot; alt=&quot;提取常量&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如添加参数判空代码：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-20-18-16.png&quot; alt=&quot;参数判空&quot; /&gt;&lt;/p&gt;

&lt;p&gt;还有更多功能都可以使用此快捷键。而且因为 Roslyn 优秀的 API，有更多扩展可以使用此快捷键生效，详见：&lt;a href=&quot;/post/develop-a-code-analyzer-for-both-nuget-and-visual-studio-extension&quot;&gt;基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;转到所有&quot;&gt;转到所有&lt;/h2&gt;

&lt;p&gt;不能每次都去解决方案里面一个个找文件，对吧！所以一个快速搜索文件和符号的快捷键也是非常能够提升效率的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + ,&lt;/code&gt; 转到所有（go to all）&lt;/p&gt;

&lt;p&gt;不过我建议将其改成：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + N&lt;/code&gt; 这是 ReSharper 默认的转到所有（Goto Everything）的快捷键&lt;/p&gt;

&lt;p&gt;这可以帮助你快速找到整个解决方案中的所有文件或符号，看下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-07-22.png&quot; alt=&quot;转到所有&quot; /&gt;&lt;/p&gt;

&lt;p&gt;修改方法可以参见：&lt;a href=&quot;/post/customizing-keyboard-shortcuts-in-visual-studio&quot;&gt;如何快速自定义 Visual Studio 中部分功能的快捷键&lt;/a&gt;，下图是此功能的命令名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;编辑.转到所有&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;Edit.GoToAll&lt;/code&gt;）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-10-04-24.png&quot; alt=&quot;编辑.转到所有&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有一些小技巧：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;你可以无需拼写完整个单词就找到你想要的符号
    &lt;ul&gt;
      &lt;li&gt;例如输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;mw&lt;/code&gt; 就可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;对于两个以上单词拼成的符号，建议将每个单词的首字母输入成大写，这样可以提高目标优先级，更容易找到
    &lt;ul&gt;
      &lt;li&gt;例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrivateTokenManager&lt;/code&gt;，如果希望干扰少一些，建议输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;PTM&lt;/code&gt; 而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ptm&lt;/code&gt;；当然想要更少的干扰，可以打更多的字母，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;priToM&lt;/code&gt; 等等&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;注意到上面的界面里面右上角有一些过滤器吗？这些过滤器有单独的快捷键。这样就直接搜索特定类型的符号，而不是所有了，可以提高查找效率。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + O&lt;/code&gt; 查找当前文件中的所有成员（只搜一个文件，这可以大大提高命中率）
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + T&lt;/code&gt; 转到符号（只搜类型名称、成员名称）
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + G&lt;/code&gt; 查找当前文件的行号（比如你在代码审查中看到一行有问题的代码，得知行号，可以迅速跳转到这一行）&lt;/p&gt;

&lt;h2 id=&quot;重构&quot;&gt;重构&lt;/h2&gt;

&lt;h3 id=&quot;重命名&quot;&gt;重命名&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-03-48.png&quot; alt=&quot;重命名&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你在一个标识符上直接重新输入改了名字，也可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Enter&lt;/code&gt; 完成重命名。&lt;/p&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;这些都可以被最上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Enter&lt;/code&gt; 替代，因此都可以忘记。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + R, Ctrl + E&lt;/code&gt; 封装字段&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + R, Ctrl + I&lt;/code&gt; 提取接口&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + R, Ctrl + V&lt;/code&gt; 删除参数&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + R, Ctrl + O&lt;/code&gt; 重新排列参数&lt;/p&gt;

&lt;h2 id=&quot;intellisense-自动完成列表&quot;&gt;IntelliSense 自动完成列表&lt;/h2&gt;

&lt;h3 id=&quot;智能感知&quot;&gt;智能感知&lt;/h3&gt;

&lt;p&gt;IntelliSense 以前有个漂亮的中文名字，叫做“智能感知”，不过现在大多数的翻译已经与以前的另一个平淡无奇的功能结合到了一起，叫做“自动完成列表”。Visual Studio 默认只会让智能感知列表发挥非常少量的功能，如果你不进行一些配置，使用起来会“要什么没什么”，想显示却不显示。&lt;/p&gt;

&lt;p&gt;请通过另一篇博客中的内容把 Visual Studio 的智能感知列表功能好好配置一下，然后我们才可以再次感受到它的强大（记得要翻到最后哦）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/let-visual-studio-empower-more-by-change-some-settings&quot;&gt;通过设置启用 Visual Studio 默认关闭的大量强大的功能提升开发效率&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果还有一些时机没有打开智能感知列表，可以配置一个快捷键打开它，我这边配置的快捷键是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + 右&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-20-23-29.png&quot; alt=&quot;设置打开智能感知的快捷键&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;参数信息&quot;&gt;参数信息&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + Shift + 空格&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;显示方法的参数信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-58-48.png&quot; alt=&quot;显示参数信息&quot; /&gt;&lt;/p&gt;

&lt;p&gt;默认在输入参数的时候就已经会显示了；如果错过了，可以在输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt; 的时候继续出现；如果还错过了，可以使用此快捷键出现。&lt;/p&gt;

&lt;h2 id=&quot;编写&quot;&gt;编写&lt;/h2&gt;

&lt;h3 id=&quot;代码格式化&quot;&gt;代码格式化&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + E&lt;/code&gt; 全文代码清理（包含全文代码格式化以及其他功能）
&lt;code class=&quot;highlighter-rouge&quot;&gt;Shift + Alt + F&lt;/code&gt; 全文代码格式化
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + F&lt;/code&gt; 格式化选定的代码&lt;/p&gt;

&lt;p&gt;关于代码清理，你可以配置做哪些事情：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-14-15.png&quot; alt=&quot;配置代码清理&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-15-06.png&quot; alt=&quot;配置代码清理&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;其他-1&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + /&lt;/code&gt; 将当前行注释或取消注释&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + C&lt;/code&gt; 将选中的代码注释掉&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + U&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + Shift + /&lt;/code&gt; 将选定的内容取消注释&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + U&lt;/code&gt; 将当前选中的所有文字转换为小写（请记得配合 F2 重命名功能使用避免编译不通过）&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + ]&lt;/code&gt; 增加行缩进&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + [&lt;/code&gt; 减少行缩进&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + S&lt;/code&gt; 保存文档
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, S&lt;/code&gt; 保存全部文档（注意按键，是按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K&lt;/code&gt; 之后所有按键松开，然后单按一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt;）&lt;/p&gt;

&lt;h2 id=&quot;导航&quot;&gt;导航&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + F&lt;/code&gt; 打开搜索面板开始强大的搜索功能&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + H&lt;/code&gt; 打开替换面板，或展开搜索面板为替换面板&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + I&lt;/code&gt; 渐进式搜索（就像 Ctrl + F 一样，不过不会抢焦点，搜索完按回车键即完成搜索，适合键盘党操作）&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + Shift + F&lt;/code&gt; 打开搜索窗口（与 Ctrl + F 虽然功能重合，但两者互不影响，意味着你可以充分这两套搜索来执行两套不同的搜索配置）&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + Shift + H&lt;/code&gt; 打开替换窗口（与 Ctrl + H 虽然功能重合，但两者互不影响，意味着你可以充分这两套替换来执行两套不同的替换配置）&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + 下&lt;/code&gt; 在当前文件中，将光标定位到下一个方法&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + 上&lt;/code&gt; 在当前文件中，将光标定位到上一个方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + M&lt;/code&gt; 将光标当前所在的类/方法切换大纲的展开或折叠&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + L&lt;/code&gt; 将全文切换大纲的展开或折叠（如果当前有任何大纲折叠了则全部展开，否则全部折叠）&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + P&lt;/code&gt; 将全文的大纲全部展开&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + U&lt;/code&gt; 将光标当前所在的类/方法大纲展开&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + M, Ctrl + O&lt;/code&gt; 将全文的大纲都折叠到定义那一层&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + D&lt;/code&gt; 查找下一个相同的标识符，然后放一个新的&lt;a href=&quot;https://zh.wikipedia.org/zh/%E8%84%B1%E5%AD%97%E7%AC%A6&quot;&gt;脱字号&lt;/a&gt;（&lt;em&gt;或者称作输入光标&lt;/em&gt;）（多次点按可以在相同字符串上出很多光标，可以一起编辑，如下图）
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + Insert&lt;/code&gt; 查找所有相同的标识符，然后全部放置&lt;a href=&quot;https://zh.wikipedia.org/zh/%E8%84%B1%E5%AD%97%E7%AC%A6&quot;&gt;脱字号&lt;/a&gt;（如下图）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-40-42.png&quot; alt=&quot;多个脱字号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh/%E8%84%B1%E5%AD%97%E7%AC%A6&quot;&gt;脱字号&lt;/a&gt; 是 Visual Studio 中对于输入光标的称呼，对应英文的 Caret。&lt;/p&gt;

&lt;h2 id=&quot;书签&quot;&gt;书签&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + K&lt;/code&gt; 为当前行加入到书签或从书签中删除
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + P&lt;/code&gt; 切换到上一个书签
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + N&lt;/code&gt; 切换到下一个书签
&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + L&lt;/code&gt; 删除所有书签（会有对话框提示的，不怕误按）&lt;/p&gt;

&lt;p&gt;如果配合书签面板，那么可以在调查问题的时候很方便在找到的各种关键代码处跳转，避免每次都寻找。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-48-30.png&quot; alt=&quot;配合书签面板&quot; /&gt;&lt;/p&gt;

&lt;p&gt;另外，还有个任务列表，跟书签列表差不多的功能：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + K, Ctrl + H&lt;/code&gt; 将当前代码加入到任务列表中或者从列表中删除（效果类似编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;// TODO&lt;/code&gt;）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-54-41.png&quot; alt=&quot;任务列表&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;显示&quot;&gt;显示&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + R, Ctrl + W&lt;/code&gt; 显示空白字符&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Z&lt;/code&gt; 切换自动换行和单行模式&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-22-18-57.png&quot; alt=&quot;显示空白字符&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;鼠标&quot;&gt;鼠标&lt;/h2&gt;

&lt;p&gt;最后提及一个，&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + 鼠标点击&lt;/code&gt; 可以跳转到定义。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-23-10-50-04.png&quot; alt=&quot;Ctrl + 鼠标点击&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过这个功能有个 bug：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/ctrl+click-not-work-when-selected.gif&quot; alt=&quot;如果要跳转的文字被选中了，就点不了了&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我报给了微软：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/1046526/when-text-is-selected-ctrlclick-will-not-perform-t.html&quot;&gt;When text is selected, &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+Click&lt;/code&gt; will not perform the &lt;code class=&quot;highlighter-rouge&quot;&gt;GoTo Definition&lt;/code&gt;. - Developer Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 May 2020 03:25:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/keyboard-shortcuts-to-improve-the-efficiency-of-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/keyboard-shortcuts-to-improve-the-efficiency-of-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：如何制作天空效果？天空盒的使用</title>
        <description>&lt;p&gt;在 Unity 编辑器的主界面中我们可以很容易制作各种场景物体，但天空如何制作呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;了解和设置默认的天空盒&quot;&gt;了解和设置默认的天空盒&lt;/h2&gt;

&lt;p&gt;在新建一个 Unity3D 项目之后，我们在空空如也的场景下看到的那蓝灰过渡的画面，其实就是 Unity3D 默认提供的“天空”，这个机制叫“天空盒”（Skybox）。&lt;/p&gt;

&lt;p&gt;你可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window-&amp;gt;Rendering-&amp;gt;Lighting Settings&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;窗口-&amp;gt;渲染-&amp;gt;照明设置&lt;/code&gt;）中打开当前场景的照明设置，里面可以设置天空效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-42-14.png&quot; alt=&quot;Lighting Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-43-13.png&quot; alt=&quot;照明设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如下图就是默认天空盒提供的各种参数：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-46-43.png&quot; alt=&quot;默认天空盒&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Unity 新建项目时默认的天空盒是&lt;strong&gt;程序天空盒（Procedural Skybox）&lt;/strong&gt;。一般来说程序天空盒提供了众多可以设置的参数，用来后期调节天空的样式。&lt;/p&gt;

&lt;p&gt;默认的天空盒其实一个参数都不能直接设置，但可以通过其他游戏物体来间接设置。例如，你试试把一开始的那个定向光（Directional Light）调一下方向，你会发现默认天空中的太阳位置也变化了，跟着定向光的方向变化。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-58-09.png&quot; alt=&quot;调节定向光的方向&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你删过这个定向光，也可以通过新建一个到场景中，然后在上图的界面里面点击选择按钮把刚刚新建的定向光选中来再次创建太阳。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-01-22.png&quot; alt=&quot;选择太阳源&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;在资源商店中下载天空盒&quot;&gt;在资源商店中下载天空盒&lt;/h2&gt;

&lt;p&gt;作为入门的话，可以考虑在资源商店中搜索并使用大神们做好的现成的天空盒（关键词 Skybox）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-04-49.png&quot; alt=&quot;在资源商店中搜索天空盒&quot; /&gt;&lt;/p&gt;

&lt;p&gt;找到喜欢的天空盒后，点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Download-&amp;gt;Import&lt;/code&gt; 可以导入到当前的项目中。这里提一点，商店中的大部分资源，导入到项目中后都在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 目录下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-09-37.png&quot; alt=&quot;Download -&amp;gt; Import&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等待下载并导入完后，你可以看到最终导入的窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-13-12.png&quot; alt=&quot;导入窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这个最终的窗口中，点击右下角的“导入”即可最终添加到项目中。&lt;/p&gt;

&lt;p&gt;在这里，你可以注意到导入的资源中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;back&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;front&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;top&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;bottom&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;left&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;right&lt;/code&gt; 这些图片。这其实是另一种天空盒——&lt;strong&gt;六面天空盒（6 Sided Skybox）&lt;/strong&gt;。（请原谅上图作者中那奇怪的缩写，因为这名字不重要。）&lt;/p&gt;

&lt;p&gt;我把刚刚下载的这款天空盒的六个面分别贴出来，就会像下图这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-30-30.png&quot; alt=&quot;天空盒的六个面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我简单做了个动图，用来直观观看天空盒的贴图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-22-skybox.gif&quot; alt=&quot;天空盒&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依然在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window-&amp;gt;Rendering-&amp;gt;Lighting Settings&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;窗口-&amp;gt;渲染-&amp;gt;照明设置&lt;/code&gt;）中打开当前场景的照明设置，然后点击选择“天空盒材质”，选择刚刚我们下载的“sky,startday”。于是，我们的场景就应用了商店下载下来的天空盒了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-19-21-26.png&quot; alt=&quot;应用商店中的天空盒&quot; /&gt;&lt;/p&gt;

&lt;p&gt;像这种六面天空盒，虽然依然可以设置太阳源，不过已经不再关联到天空中的太阳的位置了。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;本文没有介绍如何制作一个自己的天空盒，因为我也正在学习中。不过我们了解到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如何下载并应用一个现成的天空盒&lt;/li&gt;
  &lt;li&gt;天空盒的种类（六面天空盒、程序天空盒）&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 22 May 2020 10:48:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-unity3d-skybox.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-unity3d-skybox.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>专业团队：推荐一个网站，生成巨幅文字注释</title>
        <description>&lt;p&gt;通过学习本文的知识，你将可以在你的项目代码里创造大量的注释。让你的注释闪瞎团队所有人的钛合金X眼，我们是专业团队。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;
██╗    ██╗ █████╗ ██╗  ████████╗███████╗██████╗ ██╗    ██╗   ██╗
██║    ██║██╔══██╗██║  ╚══██╔══╝██╔════╝██╔══██╗██║    ██║   ██║
██║ █╗ ██║███████║██║     ██║   █████╗  ██████╔╝██║    ██║   ██║
██║███╗██║██╔══██║██║     ██║   ██╔══╝  ██╔══██╗██║    ╚██╗ ██╔╝
╚███╔███╔╝██║  ██║███████╗██║   ███████╗██║  ██║███████╗╚████╔╝ 
 ╚══╝╚══╝ ╚═╝  ╚═╝╚══════╝╚═╝   ╚══════╝╚═╝  ╚═╝╚══════╝ ╚═══╝  
&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;网址是：&lt;a href=&quot;http://patorjk.com/software/taag/&quot;&gt;http://patorjk.com/software/taag/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;你可以在里面选参数，也可以在 URL 上自己补参数以便唯一确定一个样式。比如上图样式的网址是：&lt;a href=&quot;http://patorjk.com/software/taag/#p=display&amp;amp;f=ANSI%20Shadow&amp;amp;t=walterlv&quot;&gt;http://patorjk.com/software/taag/#p=display&amp;amp;f=ANSI%20Shadow&amp;amp;t=walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;还比如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;
 ___       __   ________  ___   _________  _______   ________  ___       ___      ___ 
|\  \     |\  \|\   __  \|\  \ |\___   ___\\  ___ \ |\   __  \|\  \     |\  \    /  /|
\ \  \    \ \  \ \  \|\  \ \  \\|___ \  \_\ \   __/|\ \  \|\  \ \  \    \ \  \  /  / /
 \ \  \  __\ \  \ \   __  \ \  \    \ \  \ \ \  \_|/_\ \   _  _\ \  \    \ \  \/  / / 
  \ \  \|\__\_\  \ \  \ \  \ \  \____\ \  \ \ \  \_|\ \ \  \\  \\ \  \____\ \    / /  
   \ \____________\ \__\ \__\ \_______\ \__\ \ \_______\ \__\\ _\\ \_______\ \__/ /   
    \|____________|\|__|\|__|\|_______|\|__|  \|_______|\|__|\|__|\|_______|\|__|/    
&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尽情写到团队项目里面吧！我们是专业团队！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-19-18-42-51.png&quot; alt=&quot;专业团队&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 19 May 2020 11:47:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/patorjk-software-taag.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/patorjk-software-taag.html</guid>
        
        
        <category>miscellaneous</category>
        
      </item>
    
      <item>
        <title>.NET Framework 和 .NET Core 在默认情况下垃圾回收（GC）机制的不同（局部变量部分）</title>
        <description>&lt;p&gt;垃圾回收机制有一些未定义部分，一般来说不要依赖于这些未定义部分编程，否则容易出现一些诡异的 bug 或者不稳定的现象。&lt;/p&gt;

&lt;p&gt;本文介绍局部变量这部分的细节，而这点在 .NET Framework 和 .NET Core 默认情况下的表现有差别。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题代码&quot;&gt;问题代码&lt;/h2&gt;

&lt;p&gt;看看下面这段代码，你觉得会输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo is collected&lt;/code&gt; 吗？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;GCTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GCTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foo is collected&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你没有修改默认的编译设置，那么答案应该是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;全部 .NET Framework 下都输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo is collected&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;.NET Core 2.x 及以下输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo is collected&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;.NET Core 3.x 及以上不会有任何输出&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，.NET Core 2.1 - .NET Core 3.x 通过设置可以改变此行为，本文文末会说。&lt;/p&gt;

&lt;p&gt;然而所有这些平台编译后的 IL 都差不多。虽然引用的程序集不一样，但代码都是一样的。所以问题不在编译器，而在运行时。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Header Size: 1 byte&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Code Size: 17 (0x11) bytes&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxstack&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entrypoint&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/* (12,13)-(12,45) Program.cs */&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000251 7305000006   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newobj&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000256 730C00000A   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newobj&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;::.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x0000025B 26           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* (14,13)-(14,22) Program.cs */&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x0000025C 2802000006   */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_000B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GCTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* (15,9)-(15,10) Program.cs */&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* 0x00000261 2A           */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_0010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method Program::Main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个问题我提在了 GitHub 上，大家可以去看看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/issues/36265&quot;&gt;GC.Collect: Object without reference will be collected in .NET Framework but will NOT been collected in .NET Core · Issue #36265 · dotnet/runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;当然，当变量脱离作用域后 GC 本应回收，但在同一个函数中定义的变量是否脱离作用域却是未定义的。你可以经常在 DEBUG 下发现依然可访问的变量，但在 RELEASE 下无法访问变量就体现了这种未定义带来的行为差异。&lt;/p&gt;

&lt;p&gt;.NET Core 3.0 开始引入了分层编译（Tiered Compilation）。在开启了分层编译的情况下，JIT 执行方法时先会快速编译，随后如果此方法访问频繁会在后台优化这个编译然后替换掉之前编译的方法，以提升后续的运行性能。&lt;/p&gt;

&lt;p&gt;在分层编译被启用的情况下，GC 的行为有改变，局部变量不再及时回收。当然以后有更优化的分层编译后，可能有新的行为改变。&lt;/p&gt;

&lt;p&gt;如果要关闭分层编译，可以在项目文件中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;TieredCompilation&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，也可以设置环境变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;COMPlus_TieredCompilation=0&lt;/code&gt;。这两个是等价的。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFrameworks&amp;gt;net48;netcoreapp3.1&amp;lt;/TargetFrameworks&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;TieredCompilation&amp;gt;false&amp;lt;/TieredCompilation&amp;gt;
&lt;/span&gt;  &amp;lt;/PropertyGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于分层编译，可以阅读林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-2.1-%E4%BD%BF%E7%94%A8%E9%98%B6%E6%A2%AF%E7%BC%96%E8%AF%91.html&quot;&gt;dotnet core 2.1 使用分层编译&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文一开始说的行为改变，指的就是开关分层编译。.NET Core 2.1 开始支持分层编译但默认关闭，而 .NET Core 3.0 开始默认开启。所以在支持的框架上你可以开启或关闭。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/run-time-config/compilation&quot;&gt;Compilation config settings - .NET Core - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 May 2020 13:50:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-gc-behavior-of-local-variable.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-gc-behavior-of-local-variable.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET/Windows：删除文件夹后立即判断，有可能依然存在</title>
        <description>&lt;p&gt;如果你不了解本文的内容，可能会在未来某个时候踩坑–你可能在判断文件夹是否存在的时候得到错误的返回值。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;删除文件夹&quot;&gt;删除文件（夹）&lt;/h2&gt;

&lt;p&gt;使用 .NET 带的删除文件夹的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D:\walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者使用其他删除文件（夹）的方法，大多数是以下 Windows API 的封装：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DeleteFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPCTSTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpFileName&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveDirectoryA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPCSTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpPathName&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveDirectoryW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPCWSTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpPathName&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，大多数删除文件（夹）的代码都会遇到问题：&lt;strong&gt;文件或文件夹可能没有立即删除&lt;/strong&gt;！&lt;/p&gt;

&lt;h2 id=&quot;测试程序&quot;&gt;测试程序&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\lvyi\Desktop\Test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你猜会输出什么？每一行都输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;False&lt;/code&gt; 吗？&lt;/p&gt;

&lt;p&gt;然而实际上是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-15-15-12-13.png&quot; alt=&quot;输出&quot; /&gt;&lt;/p&gt;

&lt;p&gt;嗯……会混入少量的 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt; 在里面。这是不是有点“不符合预期”？&lt;/p&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;删除文件夹的本质方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveDirectory&lt;/code&gt; 在微软的官网中就已经解释了：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveDirectory&lt;/code&gt; 函数将标记一个文件夹在关闭后删除。这意味着在最后一个此文件夹的句柄关闭之前，此文件夹将一直不会删除。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以调用完删除文件夹的方法后，仅仅只是标记这个文件夹要删除而已。那么随后立即获取此文件夹是否存在，将取决于前面调用删除后是否真的删除了文件夹。&lt;/p&gt;

&lt;p&gt;删除文件的本质方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;DeleteFile&lt;/code&gt; 也有类似的解释：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DeleteFile&lt;/code&gt; 函数将标记一个文件在关闭后删除。这意味着在最后一个文件句柄关闭之前，此文件将一直不会删除。如果随后立即调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateFile&lt;/code&gt; 来打开一个文件的话可能会遭遇错误 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_ACCESS_DENIED&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;p&gt;因此，不要再依赖于判断文件夹是否存在来决定某个业务。例如，可以考虑创建文件夹之前不判断文件夹是否存在：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  if (Directory.Exists(path))
--  {
&lt;/span&gt;        Directory.CreateDirectory(path);
&lt;span class=&quot;gd&quot;&gt;--  }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，以上红色色块标记的代码应该删除！否则你可能会发现这段代码执行完成后，文件夹是不存在的。&lt;/p&gt;

&lt;p&gt;如果试图删除文件随后新建空白的文件或者其他文件的话，可以考虑我在另一篇博客中提到的创建或打开文件的方法，用来应对文件不存在的情况：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet-file-open-modes.html&quot;&gt;.NET 中选择合适的文件打开模式（CreateNew, Create, Open, OpenOrCreate, Truncate, Append） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-file-open-modes.html&quot;&gt;Win32 方法 CreateFile 中选择合适的文件打开模式（CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;发现 dotnet 职业技术学院另一小伙伴也遇到这个问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://xinyuehtx.github.io/post/%E4%B8%8D%E8%A6%81%E5%9C%A8Directory%E5%88%A0%E9%99%A4%E5%90%8E%E7%AB%8B%E5%88%BB%E8%B0%83%E7%94%A8Directory.Exist.html&quot;&gt;不要在Directory删除后立刻调用Directory.Exist - huangtengxiao&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya&quot;&gt;RemoveDirectoryA function (fileapi.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-deletefile&quot;&gt;DeleteFile function (winbase.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 May 2020 12:10:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/file-or-directory-delete-is-not-completed-after-calling-delete.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/file-or-directory-delete-is-not-completed-after-calling-delete.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在制作多框架项目的 NuGet 包时应该注意的问题（buildMultiTargeting/TargetFrameworks）</title>
        <description>&lt;p&gt;制作一个 dll 引用的 NuGet 包简直是一键完成，无论是不是多框架项目；制作 dotnet-tools 也是如此。但如果需要自定义一些编译步骤，那么就需要在制作 NuGet 包时做很多的特殊处理了。&lt;/p&gt;

&lt;p&gt;本文介绍制作适用于多框架项目的 NuGet 工具包时应该注意的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景知识&quot;&gt;背景知识&lt;/h2&gt;

&lt;h3 id=&quot;nuget-包内的文件夹结构&quot;&gt;NuGet 包内的文件夹结构&lt;/h3&gt;

&lt;p&gt;回顾一下 NuGet 包的文件夹结构：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+ /
+ lib/
+ ref/
+ runtimes/
+ content/
+ build/
+ buildMultiTargeting/
+ buildTransitive
+ tools/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于涉及到自定义 NuGet 包的代码都写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildTransitive&lt;/code&gt; 中，其他都不涉及到 NuGet 包在编译期间会做的事情，另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;buildTransitive&lt;/code&gt; 是用来&lt;a href=&quot;https://github.com/NuGet/Home/wiki/Allow-package--authors-to-define-build-assets-transitive-behavior&quot;&gt;处理包传递过程中的编译过程&lt;/a&gt;的，所以我们本文只说也只需要说 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这里面的代码都是用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 写出来的，如果你对此不了解，建议阅读这些博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj.html&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;制作有自定义功能的-nuget-包&quot;&gt;制作有自定义功能的 NuGet 包&lt;/h3&gt;

&lt;p&gt;我之前写过一些关于如何制作各种高级功能的 NuGet 包的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;按照上面的博客制作出来的 NuGet 包其实是适用于单框架项目和多框架项目的，甚至也适用于传统的非 SDK 风格的项目。&lt;/p&gt;

&lt;p&gt;关于单框架和多框架项目，就是项目文件中这里的差别：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 单框架项目 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 多框架项目 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp3.1;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，有的小伙伴希望探索一些更高级的用法，所以可能会遇到在多框架项目中，NuGet 包自定义的功能不执行的问题。&lt;/p&gt;

&lt;p&gt;接下来，我们了解一下在单框架和多框架下 NuGet 包执行上的不同。&lt;/p&gt;

&lt;h2 id=&quot;执行时机&quot;&gt;执行时机&lt;/h2&gt;

&lt;p&gt;我们打出这样的两种 NuGet 包，一种是仅包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹而不包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹；一种是包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹。&lt;/p&gt;

&lt;p&gt;我们的目标项目一种是单框架项目；一种是多框架项目。&lt;/p&gt;

&lt;p&gt;于是我们可以得到这样的四种不同的组合情况：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;仅含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹的 NuGet 包装到单框架项目中&lt;/li&gt;
  &lt;li&gt;仅含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹的 NuGet 包装到多框架项目中&lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹的 NuGet 包装到单框架项目中&lt;/li&gt;
  &lt;li&gt;包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹的 NuGet 包装到多框架项目中&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;1-仅含-build-文件夹的-nuget-包装到单框架项目中&quot;&gt;1. 仅含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹的 NuGet 包装到单框架项目中&lt;/h3&gt;

&lt;p&gt;在这种情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件在目标项目编译时正常执行。&lt;/p&gt;

&lt;h3 id=&quot;2-仅含-build-文件夹的-nuget-包装到多框架项目中&quot;&gt;2. 仅含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹的 NuGet 包装到多框架项目中&lt;/h3&gt;

&lt;p&gt;在这种情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件，会分别在目标项目编译每个框架的时候执行一次。&lt;/p&gt;

&lt;p&gt;例如这种项目：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 多框架项目 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp3.1;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NullableAttributes.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.15.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，在编译 &lt;code class=&quot;highlighter-rouge&quot;&gt;netcoreapp3.1&lt;/code&gt; 框架的时候会执行一次 Walterlv.NullableAttributes.Source 包中 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的编译任务；在编译 &lt;code class=&quot;highlighter-rouge&quot;&gt;net48&lt;/code&gt; 框架的时候又会执行一次 Walterlv.NullableAttributes.Source 包中 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的编译任务。&lt;/p&gt;

&lt;h3 id=&quot;3-包含-build-和-buildmultitargeting-文件夹的-nuget-包装到单框架项目中&quot;&gt;3. 包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹的 NuGet 包装到单框架项目中&lt;/h3&gt;

&lt;p&gt;在这种情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 中的任何编译任务相当于不存在。编译过程与情况 1 是完全一样的。&lt;/p&gt;

&lt;h3 id=&quot;4-包含-build-和-buildmultitargeting-文件夹的-nuget-包装到多框架项目中&quot;&gt;4. 包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹的 NuGet 包装到多框架项目中&lt;/h3&gt;

&lt;p&gt;从 NuGet 5.x 版本开始在这种情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 中的内容和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 中的编译任务会同时参与编译。&lt;/p&gt;

&lt;p&gt;依然举例这样的目标项目（不过使用了含 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 的 NuGet 包）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 多框架项目 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp3.1;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NullableAttributes.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.1.1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译一开始，会将 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 中的编译任务加入执行。在编译 &lt;code class=&quot;highlighter-rouge&quot;&gt;netcoreapp3.1&lt;/code&gt; 框架的时候会执行一次 Walterlv.NullableAttributes.Source 包中 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的编译任务；在编译 &lt;code class=&quot;highlighter-rouge&quot;&gt;net48&lt;/code&gt; 框架的时候又会执行一次 Walterlv.NullableAttributes.Source 包中 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中的编译任务。而这两个单独框架的编译结束后，&lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 中的任务才会结束。&lt;/p&gt;

&lt;p&gt;也就是说，这两个编译任务文件夹中的编译任务是都会执行的。但是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;两者参与编译的 Targets 不一样&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;下表中列出了在你没有编写任何扩展的任务或者干预已有 Target 执行的情况下，默认可以依赖的 Target（指的是可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets=&quot;xx&quot;&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets=&quot;xx&quot;&lt;/code&gt; 的方式扩展编译任务：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;可依赖的 Target&lt;/th&gt;
      &lt;th&gt;build&lt;/th&gt;
      &lt;th&gt;buildMultiTargeting&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;BeforeCompile&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Compile&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CoreCompile&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;AfterCompile&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BeforeBuild&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Build&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;AfterBuild&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BeforeRebuild&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Rebuild&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔（如果强行执行）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;AfterRebuild&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BeforeClean&lt;/td&gt;
      &lt;td&gt;✔（如果强行执行）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Clean&lt;/td&gt;
      &lt;td&gt;✔（如果强行执行）&lt;/td&gt;
      &lt;td&gt;✔（如果强行执行）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;AfterClean&lt;/td&gt;
      &lt;td&gt;✔（如果强行执行）&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;注：强制执行说的是一般编译时不会执行，你需要在命令中指定执行这个 Target。也对应到 Visual Studio 里的“重新编译”和“清理”的功能。&lt;/p&gt;

&lt;p&gt;为了更好理解上表，这里给出一个例子。下面的代码如果在 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹中则会在编译过程输出一堆星号，而如果在 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹中则不会执行。而无论目标项目是否是多框架的。但换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt; 则会两个文件夹中都输出。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;****************************************************************&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，不要被这个第 4 种情况带歪了！如果你的 NuGet 包依然只有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹，那么上面的所有 Targets 都是会执行的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package&quot;&gt;Create a NuGet package using nuget.exe CLI - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/Home/wiki/Allow-package--authors-to-define-build-assets-transitive-behavior&quot;&gt;Allow package authors to define build assets transitive behavior · NuGet/Home Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 May 2020 06:23:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/build-multi-targeting-nuget-package.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/build-multi-targeting-nuget-package.html</guid>
        
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>为 .NET 各种开发工具设置网络代理，提升在大陆的网络性能</title>
        <description>&lt;p&gt;git、nuget、scoop 如何设置网络代理提升网络访问速度呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;在下面的博客正文中，都假设我的本机搭设了代理服务，其中 SOCKS5 代理服务的端口号是 7777，HTTP 代理服务的端口号是 7778。&lt;/p&gt;

&lt;h2 id=&quot;git&quot;&gt;git&lt;/h2&gt;

&lt;h3 id=&quot;使用命令行设置&quot;&gt;使用命令行设置&lt;/h3&gt;

&lt;p&gt;git 支持设置 http 代理和 socks5 代理，http 的代理和 https 的代理是分开设置的。&lt;/p&gt;

&lt;p&gt;设置方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;打开任意一个命令行工具；&lt;/li&gt;
  &lt;li&gt;在命令行中输入以下两个命令并回车。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http.proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http://127.0.0.1:7778&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https.proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http://127.0.0.1:7778&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http.proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;socks5://127.0.0.1:7777&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https.proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;socks5://127.0.0.1:7777&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在设置完成之后，&lt;strong&gt;你无需担心内网访问的问题&lt;/strong&gt;，因为 Git 会自动在代理的时候过滤掉内网代理。&lt;/p&gt;

&lt;h3 id=&quot;直接修改配置文件&quot;&gt;直接修改配置文件&lt;/h3&gt;

&lt;p&gt;git 全局配置文件的路径在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%USERPROFILE%\.gitconfig&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在这个文件中，你需要添加以下几行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    [user]
        name = walterlv
        email = walter.lv@qq.com
&lt;span class=&quot;gi&quot;&gt;+   [http]
+       proxy = http://127.0.0.1:7778
+   [https]
+       proxy = http://127.0.0.1:7778
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    [user]
        name = walterlv
        email = walter.lv@qq.com
&lt;span class=&quot;gi&quot;&gt;+   [http]
+       proxy = socks5://127.0.0.1:7777
+   [https]
+       proxy = socks5://127.0.0.1:7777
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;添加完成之后，你的 git 访问就会走代理，对于 GitHub 的访问，快速克隆大型仓库是非常有帮助的。&lt;/p&gt;

&lt;h3 id=&quot;使用-tortoisegit-设置&quot;&gt;使用 TortoiseGit 设置&lt;/h3&gt;

&lt;p&gt;以上命令行的方法是最简单的，然而你也可以使用其他的工具设置，比如 TortoiseGit。&lt;/p&gt;

&lt;p&gt;设置方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在任意的文件夹中打开 TortoiseGit 的设置页面，然后定位到网络一栏中，勾选“使用代理服务器”。&lt;/li&gt;
  &lt;li&gt;输入服务器地址和端口号，确定即可。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-03-08-04-35.png&quot; alt=&quot;在 TortoiseGit 中设置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;nuget&quot;&gt;NuGet&lt;/h2&gt;

&lt;p&gt;使用 NuGet 直接拉取 &lt;nuget.org&gt; 的内容也是很慢的，如果有一个代理服务器的设置那么也能大大提速。&lt;/nuget.org&gt;&lt;/p&gt;

&lt;h3 id=&quot;在命令行中设置&quot;&gt;在命令行中设置&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http_proxy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http://127.0.0.1:7778&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;直接修改配置文件-1&quot;&gt;直接修改配置文件&lt;/h3&gt;

&lt;p&gt;git 全局配置文件的路径在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%APPDATA%\NuGet\NuGet.Config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在这个文件中，你需要添加以下几行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;configuration&amp;gt;
      &amp;lt;packageSources&amp;gt;
        &amp;lt;add key=&quot;nuget.org&quot; value=&quot;https://api.nuget.org/v3/index.json&quot; protocolVersion=&quot;3&quot; /&amp;gt;
      &amp;lt;/packageSources&amp;gt;
      &amp;lt;config&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+       &amp;lt;add key=&quot;http_proxy&quot; value=&quot;http://127.0.0.1:7778&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/config&amp;gt;
    &amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果这么设置了，那么 NuGet 会为所有的包源设置代理。然而内部部署的包源并不需要代理，于是还需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;no_proxy&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;configuration&amp;gt;
      &amp;lt;config&amp;gt;
        &amp;lt;add key=&quot;http_proxy&quot; value=&quot;http://127.0.0.1:7778&quot; /&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+       &amp;lt;add key=&quot;no_proxy&quot; value=&quot;localhost,127.0.0.1,*.walterlv.com&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/config&amp;gt;
    &amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;scoop&quot;&gt;Scoop&lt;/h2&gt;

&lt;p&gt;Scoop 是一款优秀的包管理工具，可以以绿色的方式安装各种工具。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;127.0.0.1:7778&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;chocolatey--chocolateygui&quot;&gt;Chocolatey / ChocolateyGUI&lt;/h2&gt;

&lt;p&gt;Chocolatey 是 Windows 上非常著名的包管理工具。它支持的代理设置方法非常多，你可以去它的官网了解所有的设置代理的方法：&lt;a href=&quot;https://chocolatey.org/docs/proxy-settings-for-chocolatey&quot;&gt;Chocolatey Software - Proxy Settings for Chocolatey&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这里简单搬运一下直接的设置方法。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;choco&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;127.0.0.1:7778&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同时，它也支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;http_proxy&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;https_proxy&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;no_proxy&lt;/code&gt; 这样全局的环境变量设置。&lt;/p&gt;

&lt;p&gt;另外，不喜欢命令行版的 &lt;code class=&quot;highlighter-rouge&quot;&gt;choco&lt;/code&gt; 和命令行版代理设置的同学，可以考虑用 ChocolateyGUI：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/chocolatey/ChocolateyGUI&quot;&gt;chocolatey/ChocolateyGUI: A delicious GUI for Chocolatey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-13-10-07-48.png&quot; alt=&quot;ChocolateyGUI 中的代理设置&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file&quot;&gt;nuget.config File Reference - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/Home/issues/3776&quot;&gt;no_proxy containing wildcard breaks nuget · Issue #3776 · NuGet/Home&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/lukesampson/scoop/wiki/Using-Scoop-behind-a-proxy&quot;&gt;Using Scoop behind a proxy · lukesampson/scoop Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/41623825/6233938&quot;&gt;github - Only use a proxy for certain git urls/domains? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/evantoli/f8c23a37eb3558ab8765&quot;&gt;Configure Git to use a proxy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chocolatey.org/docs/proxy-settings-for-chocolatey&quot;&gt;Chocolatey Software - Proxy Settings for Chocolatey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 13 May 2020 02:09:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/setting-up-proxies-for-development-tools.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/setting-up-proxies-for-development-tools.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：如何在脚本中找到游戏对象的父子级/祖孙级对象和它们的组件</title>
        <description>&lt;p&gt;在真正能玩的游戏场景中，很多脚本的执行是在不确定的游戏对象上进项的，于是会考虑在父对象或者子对象上去写脚本。这时，可能需要查找游戏对象。那么如何在脚本中找到父子游戏对象（gameObject）呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;场景&quot;&gt;场景&lt;/h2&gt;

&lt;p&gt;如下图所示，&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows&lt;/code&gt; 游戏对象下面可能有很多不确定数量和位置的游戏对象，需要操作它们。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-10-15-16-04.png&quot; alt=&quot;游戏场景&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在为游戏对象创建脚本的时候，这个脚本中的类会继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;MonoBehavior&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowUpdater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;找父组件子组件&quot;&gt;找父组件/子组件&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MonoBehavior&lt;/code&gt; 直接提供了查找父子组件的方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetComponent(s)&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;GetComponent(s)InParent&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetComponent(s)InChildren&lt;/code&gt;，因此直接调用即可。对于泛型方法，每个子对象只会找到一个组件，所以通常适用于子组件非常简单的场景。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetComponentsInChildren&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;renderers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;material&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mainTexture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;找父对象子对象&quot;&gt;找父对象/子对象&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MonoBehavior&lt;/code&gt; 并没有提供直接查找父子对象的方法。&lt;/p&gt;

&lt;p&gt;但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt; 有！&lt;/p&gt;

&lt;p&gt;所以，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt; 可以间接获取到子对象。&lt;code class=&quot;highlighter-rouge&quot;&gt;GetChild()&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;GetChildCount&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gameObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetActive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 10 May 2020 07:34:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-get-unity-game-objects-or-components-through-scene-hierarchy.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-get-unity-game-objects-or-components-through-scene-hierarchy.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>WPF 中使用附加属性，将任意 UI 元素或控件裁剪成圆形（椭圆）</title>
        <description>&lt;p&gt;不知从什么时候开始，头像流行使用圆形了，于是各个平台开始追逐显示圆形裁剪图像的技术。WPF 作为一个优秀的 UI 框架，当然有其内建的机制支持这种圆形裁剪。&lt;/p&gt;

&lt;p&gt;不过，内建的机制仅支持画刷，而如果被裁剪的元素支持交互，或者拥有普通画刷无法达到的显示效果，那么就需要本文介绍的更加通用的解决方法了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;UWP 的圆形裁剪请左转参考&lt;/em&gt;：&lt;a href=&quot;/post/clip-uwp-image-to-ellipse&quot;&gt;UWP 将图片裁剪成圆形（椭圆）&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Clip&lt;/code&gt; 依赖项属性，可以使用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt; 来裁剪任意的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt;。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt; 几乎可以表示任意形状，这意味着我们可以才建成任意想要的样子。&lt;/p&gt;

&lt;p&gt;于是，我们可以利用这一点，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EllipseGeometry&lt;/code&gt; 将任意 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 裁剪成圆形或者椭圆形。比如，写成下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Clip&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Center=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;120 180&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;180&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Clip&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;171,172,51,21&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终可以出现如下的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-15-09-51-13.png&quot; alt=&quot;裁剪成椭圆&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过，稍微改变下窗口的大小，就会发现裁剪的范围不对了。因为我们写死了圆形裁剪的中心点和两个不同方向的半径（这里可不好说是长半轴还是短半轴啊）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-15-09-51-56.png&quot; alt=&quot;没用跟着改变大小的圆形裁剪&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们需要一个可以自动修改裁剪圆形的一种机制，于是，我们想到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt;。为了使 XAML 的代码好看一点，我将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 封装到了一个单独的类中处理，使用附加属性提供 API。&lt;/p&gt;

&lt;p&gt;我封装好的类如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 提供将任意控件裁剪为圆形或椭圆的附加属性。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EllipseClipper&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 标识 IsClipping 的附加属性。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsClippingProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;IsClipping&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EllipseClipper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnIsClippingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetIsClipping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsClippingProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetIsClipping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsClippingProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnIsClippingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果 IsClipping 附加属性被设置为 false，则清除 UIElement.Clip 属性。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClearValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClipProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 如果 UIElement.Clip 属性被用作其他用途，则抛出异常说明问题所在。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EllipseGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EllipseClipper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsClippingProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;$&quot;is using &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClipProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;for clipping, dont use this property manually.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 使用 UIElement.Clip 属性。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EllipseGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 使用绑定来根据控件的宽高更新椭圆裁剪范围。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xBinding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneWay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Converter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HalfConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yBinding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneWay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Converter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HalfConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xyBinding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MultiBinding&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Converter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SizeToClipCenterConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;xyBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bindings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;xyBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bindings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EllipseGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadiusXProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EllipseGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadiusYProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ellipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EllipseGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CenterProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xyBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SizeToClipCenterConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMultiValueConverter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertBack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HalfConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IValueConverter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertBack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 XAML 中只需要很简单的一个属性赋值即可达到圆形或椭圆形裁剪。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;local:EllipseClipper.IsClipping=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fluentdesign-app-header.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;171,172,51,21&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而且在控件的大小改变的时候也能够正常更新裁剪范围。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-15-ellipse-clip.gif&quot; alt=&quot;裁剪成椭圆&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这篇博客的核心代码我也贴在了 StackOverflow 上：&lt;a href=&quot;https://stackoverflow.com/a/50867867/6233938&quot;&gt;c# - WPF displaying a gif in an ellipse - Stack Overflow&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 06 May 2020 00:15:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/clip-wpf-uielement-to-ellipse.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/clip-wpf-uielement-to-ellipse.html</guid>
        
        
        <category>xaml</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>快速在 Windows 上搭建 Jekyll 开发环境</title>
        <description>&lt;p&gt;Jekyll 是一个不错的静态博客工具，本文将提供快速在 Windows 系统上搭建 Jekyll 开发环境的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;for-老手&quot;&gt;For 老手&lt;/h2&gt;

&lt;p&gt;其实所需的命令只有少数几个而已：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 全局命令：安装 Ruby 所需的依赖&lt;/span&gt;
ridk &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 全局命令：安装 Jekyll&lt;/span&gt;
gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jekyll bundler
&lt;span class=&quot;c&quot;&gt;# 工作目录命令：安装博客仓库中所需的依赖&lt;/span&gt;
bundle &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 工作目录命令：将本地博客仓库跑起来&lt;/span&gt;
jekyll serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;for-新手&quot;&gt;For 新手&lt;/h2&gt;

&lt;h3 id=&quot;下载必要的软件&quot;&gt;下载必要的软件&lt;/h3&gt;

&lt;p&gt;考虑到我们的网络环境，为了提升本文的阅读效率，建议一边下载一边阅读。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubyinstaller.org/downloads/&quot;&gt;Ruby（挑最新的下载即可）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://repo.msys2.org/distrib/x86_64/msys2-x86_64-20161025.exe&quot;&gt;MSYS2（虽然这不是必要的，但能大大提高配置环境的成功率和速度）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果无法下载，可以考虑 Docker 或者 Windows 上的其他包管理器（如 scoop）。&lt;/p&gt;

&lt;h3 id=&quot;安装-ruby-和-jekyll&quot;&gt;安装 Ruby 和 Jekyll&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;分别安装下载好的 Ruby 和 MSYS2 安装包，一路下一步，直到两者都安装结束；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;一般安装完 Ruby 后会自动弹出一个新的命令行安装界面，我们需要在里面选择 3，然后回车。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2018-03-04-12-14-41.png&quot; alt=&quot;ridk install&quot; /&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;如果上一步没有提前下载安装 MSYS2，那么这里会因为众所周知的网络原因速度奇慢无比，或者以失败告终。&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;如果没有弹出命令行安装界面或者把它关掉了，那么也可以在任意的命令行中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;ridk install&lt;/code&gt; 来再次进入命令行安装界面。&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;如果网络状况良好，能够一次装成功。（如果不幸失败。则一直再次选 3 继续安装直到全部成功为止。）&lt;/em&gt;
        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Install MSYS2 and MINGW development toolchain succeeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;再打开一个新的命令行窗口（cmd/powershell/bash/msys2 都行），输入以下命令安装 jekyll：
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jekyll bundler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;自此，Jekyll 开发环境就搭建完成了。&lt;/p&gt;

&lt;h3 id=&quot;让自己的博客跑起来&quot;&gt;让自己的博客跑起来&lt;/h3&gt;

&lt;p&gt;如果你已经有了自己的 Jekyll 博客，希望在本地能够编译运行，那么就继续阅读本节。&lt;/p&gt;

&lt;p&gt;可能你的博客来源于这些地方：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;从 &lt;a href=&quot;http://jekyllthemes.org/&quot;&gt;Jekyll Themes&lt;/a&gt; 挑选并下载了一款主题；&lt;/li&gt;
  &lt;li&gt;克隆了&lt;a href=&quot;https://blog.lindexi.com/post/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E6%9C%AC%E6%A8%A1%E6%9D%BF%E6%90%AD%E5%BB%BA%E5%8D%9A%E5%AE%A2.html&quot;&gt;自己或别人的博客站点&lt;/a&gt;，准备改改自己用；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么我们开始。首先在博客的根目录打开命令行，接下来的操作都在命令行中。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;安装依赖包
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;将 Jekyll 服务跑起来
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jekyll serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这就完成了！&lt;/p&gt;

&lt;p&gt;不过，如果上述第 2 个步骤发生了错误，通常是依赖包的版本不匹配所致，运行命令更新依赖包：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后再次 &lt;code class=&quot;highlighter-rouge&quot;&gt;jekyll serve&lt;/code&gt; 即可。&lt;/p&gt;

&lt;!--
## For 懒人

懒人也不要太懒啊，最起码得翻到这篇文章的最末尾吧？
--&gt;

&lt;h2 id=&quot;ruby-跨版本升级的坑&quot;&gt;Ruby 跨版本升级的坑&lt;/h2&gt;

&lt;p&gt;如果你之前安装过 Ruby 的旧版本，现在需要跨大版本号升级，那么你会遇到很多问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ruby&lt;/code&gt; 命令对应新旧哪个版本是不明确的&lt;/li&gt;
  &lt;li&gt;如果你覆盖安装了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ruby&lt;/code&gt;，那么之前安装自动设置的那些环境变量（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;gem&lt;/code&gt;）就会丢失&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这会导致你试图编译你的博客时遇到各种各样奇怪的错误。&lt;/p&gt;

&lt;p&gt;所以，你需要做的是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;卸载掉之前的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ruby&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;再次安装 &lt;code class=&quot;highlighter-rouge&quot;&gt;ruby&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 06 May 2020 00:13:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/setup-jekyll-in-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/setup-jekyll-in-windows.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：Unity Editor 编辑器常用快捷键</title>
        <description>&lt;p&gt;本文为 Unity3D 入门小伙伴整理 Unity 编辑器中的常用快捷键。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;调节工具&quot;&gt;调节工具&lt;/h2&gt;

&lt;p&gt;Unity 编辑器左上角的一组按钮，正好也对应着键盘左上角的字母：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;图标&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-29-17.png&quot; alt=&quot;Q&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-29-44.png&quot; alt=&quot;W&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-30-06.png&quot; alt=&quot;E&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-30-27.png&quot; alt=&quot;R&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-30-46.png&quot; alt=&quot;T&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-31-06.png&quot; alt=&quot;Y&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;快捷键&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Q&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;W&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;E&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;R&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;T&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Y&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;英文&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Hand Tool&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Move Tool&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Rotate Tool&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Scale Tool&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Rect Tool&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Move, Rotate or Scale selected objects&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;中文&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;手形工具&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;移动工具&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;旋转工具&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;缩放工具&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;矩形工具&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;移动、旋转或缩放选定对象&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;功能&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;在整个场景中移动漫游&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;按坐标轴移动选定对象&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;按三个维度旋转对象&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;在三个维度上缩放对象&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;以矩形的方式调节对象的尺寸&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;综合前面所有对选定对象的调节工具&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;除了使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; 打开手形工具随后用鼠标左键漫游场景外，使用鼠标中键也可以在任意工具下漫游场景（按住鼠标中键然后移动鼠标）。&lt;/p&gt;

&lt;p&gt;按住鼠标右键移动可以以当前镜头处为轴心旋转视角，按住鼠标右键的同时按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;W&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;D&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;E&lt;/code&gt; 也可以前后左右下上移动镜头。&lt;/p&gt;

&lt;p&gt;按住 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt&lt;/code&gt; 键的同时，也可以在任意工具下使用鼠标左键移动镜头，不过与前面不同的是，这是以目标物体为轴心来移动和旋转的。&lt;/p&gt;

&lt;p&gt;按住 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt&lt;/code&gt; 键的同时，按住鼠标右键上下左右移动也可以移远和移近物体。&lt;/p&gt;

&lt;p&gt;按住 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl&lt;/code&gt; 键的同时，使用以上所有工具移动、旋转或缩放对象的话，可以对齐网格。（&lt;code class=&quot;highlighter-rouge&quot;&gt;Edit-&amp;gt;Grid and Snap Settings...&lt;/code&gt; 可以打开网格设置。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-13-57.png&quot; alt=&quot;网格设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;按住 &lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; 键的同时，鼠标放到对象中心的移动格子上移动对象，可以让此对象对齐场景中的其他对象。（下图指示了鼠标拖哪里。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-15-59.png&quot; alt=&quot;对齐其他对象&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;视图调节&quot;&gt;视图调节&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;F&lt;/strong&gt;（置于中心）：当在层级（Hierarchy）窗口或场景（Scene）窗口选中某个对象后，可按 &lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 将对象置于场景中心，并放大/缩小到合适的尺寸。&lt;/p&gt;

&lt;h2 id=&quot;窗口调整&quot;&gt;窗口调整&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shift + 空格&lt;/strong&gt;（最大化/还原）：当你的焦点在 Unity 编辑器的任何子窗口中的时候，按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Shift + Space&lt;/code&gt; 可以将此子窗口最大化或者还原。&lt;/p&gt;

&lt;p&gt;如下图是最大化后的场景窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-17-43-49.png&quot; alt=&quot;最大化场景窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;进入退出播放模式&quot;&gt;进入退出播放模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ctrl + P&lt;/strong&gt;（进入退出播放模式）：相当于按下界面中的“播放”按钮。&lt;/p&gt;

&lt;h2 id=&quot;所有快捷键&quot;&gt;所有快捷键&lt;/h2&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Edit-&amp;gt;Shortcuts...&lt;/code&gt; 中可以找到并编辑所有的快捷键。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-18-20-42.png&quot; alt=&quot;所有快捷键&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.unity3d.com/Manual/UnityHotkeys.html&quot;&gt;Unity - Manual: Unity shortcuts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 May 2020 11:35:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-unity-editor-shortcut-keys.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-unity-editor-shortcut-keys.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：让 C# 脚本公开可在 Unity 编辑器中设置的属性</title>
        <description>&lt;p&gt;将一部分参数从 C# 脚本中抽离出来，可以让 C# 脚本在 Unity 项目中更通用，适用于更多游戏对象（gameObject）。&lt;/p&gt;

&lt;p&gt;本文介绍如何创建可在 Unity 编辑器中设置属性的 C# 脚本，并介绍如何在 Unity 编辑器中设置它们。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;简单的-c-脚本&quot;&gt;简单的 C# 脚本&lt;/h2&gt;

&lt;p&gt;本文的例子取自于我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-handle-base-player-input-for-movement.html&quot;&gt;Unity3D 入门：最简单的控制视角，以及控制角色前进、转向的脚本 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlayerController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wsValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vertical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Mouse X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveDirection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wsValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Translate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;moveDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normalized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Space&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;公开字段&quot;&gt;公开字段&lt;/h2&gt;

&lt;p&gt;只需要将脚本的字段设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt;，Unity 编辑器便能识别出这些字段以及它们的类型，然后允许你在 Inspector 中编辑它们。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-16-14-41.png&quot; alt=&quot;在 Inspector 中设置公开字段的值&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意，Inspector 中不会识别属性，更不会识别方法。所以要公开，必须使用“字段”。&lt;/p&gt;

&lt;h2 id=&quot;在哪里修改值&quot;&gt;在哪里修改值&lt;/h2&gt;

&lt;p&gt;Unity 编辑器会在每次重新激活编辑器窗口的时候重新加载 Unity 项目。因此，当你在 Visual Studio 或其他编辑器中新编写了公开字段后，回到 Unity 编辑器中便会识别到这些字段，然后显示出来。&lt;/p&gt;

&lt;p&gt;值得注意的是，这个时候就已经记录了此脚本在此游戏对象中的值。也就是说，此后无论你如何在脚本中修改公开字段的值，运行游戏都不会有变化，因为游戏开始后，就会用你在编辑器中设置的值（虽然不是手工设的）覆盖脚本中编写的默认值。&lt;/p&gt;

&lt;p&gt;要修改，还是需要在 Inspector 中去修改值。&lt;/p&gt;

&lt;h2 id=&quot;有趣的名称&quot;&gt;有趣的名称&lt;/h2&gt;

&lt;p&gt;按照 Unity C# 脚本的编写规范，公开的字段也是按 &lt;code class=&quot;highlighter-rouge&quot;&gt;camelCase&lt;/code&gt; 命名的。当然，你也可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PascalCase&lt;/code&gt; 命名也不会有什么识别上的问题。&lt;/p&gt;

&lt;p&gt;不过，无论你用什么命名，Inspector 中都会将你的名称拆开成多个单词，并首字母大写。&lt;/p&gt;

&lt;p&gt;更有趣的是，如果你使用了一些预设的字段名称，那么 Inspector 中会显示成预设的名称。典型的是命名成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt; 的时候，Inspector 中会显示“名称”（如果你装了中文语言包的话）。&lt;/p&gt;
</description>
        <pubDate>Tue, 05 May 2020 08:22:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-public-fields-of-unity-script.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-public-fields-of-unity-script.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：最简单的控制视角，以及控制角色前进、转向的脚本</title>
        <description>&lt;p&gt;本文依然是 Unity3D 的入门篇。作为 Unity3D 的入门读者，你可能希望迅速让你能在游戏中操作你的视角，或者让角色移动。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建脚本&quot;&gt;创建脚本&lt;/h2&gt;

&lt;p&gt;作为入门篇，可能需要讲一下如何创建脚本。按下图，在 Unity 编辑器中：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Hierarchy&lt;/code&gt;（层级）中选中主摄像机；&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inspector&lt;/code&gt;（检查器）中选择最后那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add Component&lt;/code&gt;（添加组件）；&lt;/li&gt;
  &lt;li&gt;选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;New Script&lt;/code&gt;（新建脚本）输入脚本名称，然后点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Create and Add&lt;/code&gt;（创建并添加）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-15-29-26.png&quot; alt=&quot;创建并添加脚本&quot; /&gt;&lt;/p&gt;

&lt;!-- 如果加错了，可以像这样删除脚本

![](/static/posts/2020-05-05-15-32-56.png) --&gt;

&lt;p&gt;接下来，我们需要去 Visual Studio 中编辑这个脚本。&lt;/p&gt;

&lt;p&gt;点击菜单中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Open C# Project&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;资源&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;打开 C# 项目&lt;/code&gt;）。于是可以转到 Visual Studio 中编辑你的脚本文件。&lt;/p&gt;

&lt;h2 id=&quot;最简代码&quot;&gt;最简代码&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 中找到我们刚刚创建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PlayerController&lt;/code&gt; 脚本。&lt;/p&gt;

&lt;p&gt;其实就是脚本名加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;.cs&lt;/code&gt; 后缀。Unity 中 C# 脚本要求文件名必须匹配脚本的类名，因此，不要随便尝试改文件名或类名；就算改了，也要同步更新文件名和类名重新匹配，并重新在 Inspector 中添加新名称的脚本。&lt;/p&gt;

&lt;p&gt;在脚本中添加如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UnityEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlayerController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MonoBehaviour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wsValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vertical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Mouse X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveDirection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wsValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Translate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;moveDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normalized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Space&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateSpeed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Start&lt;/code&gt; 消息会在游戏对象（也就是前面我们添加了脚本的那个主摄像机）创建后，第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 消息执行前调用，可以用来做一些初始化。这里，我们拿到我们需要做变换的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt; 组件（这里的组件也就是 Unity 编辑器的“检查器”中看到的一个个组件）。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 会尽量在每一帧执行一次，我们在这里执行一些需要每帧更新的逻辑。&lt;/p&gt;

&lt;p&gt;我们做了这些事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;定义了公共的两个字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;moveSpeed&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;rotateSpeed&lt;/code&gt; 表示移动速度和转向速度。&lt;/li&gt;
  &lt;li&gt;通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Input.GetAxis&lt;/code&gt; 获取不同种类的玩家输入。&lt;/li&gt;
  &lt;li&gt;通过玩家的输入计算 &lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt; 组件的更新差量，然后更新 &lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt; 组件。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于在脚本当中公开属性以在编辑器中设置的更多细节，可阅读我的另一篇 Unity3D 入门博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/unity-starter-public-fields-of-unity-script.html&quot;&gt;Unity3D 入门：让 C# 脚本公开可在 Unity 编辑器中设置的属性 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以看以下效果（gif 文件有点大，多等等）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/post/2020-05-05-player-controller.gif&quot; alt=&quot;运动效果&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 05 May 2020 07:58:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-handle-base-player-input-for-movement.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-handle-base-player-input-for-movement.html</guid>
        
        
        <category>unity</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：安装 Unity3D 并配置与 Visual Studio 的协作开发环境</title>
        <description>&lt;p&gt;实际上本文不看也罢，因为整个过程除了网速之外基本没啥坑。不过装完可能有一些配置，所以如果不知道的话可以参考本文。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;

&lt;p&gt;我们共需要安装两款应用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visual Studio 2019 及 Unity 编辑器组件&lt;/li&gt;
  &lt;li&gt;Unity Hub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这两款应用安装不分先后，不过建议全部都安装完后再启动，避免单独启动无法运行使用的问题。&lt;/p&gt;

&lt;h3 id=&quot;安装-visual-studio-2019-的-unity-编辑器组件&quot;&gt;安装 Visual Studio 2019 的 Unity 编辑器组件。&lt;/h3&gt;

&lt;p&gt;Visual Studio 2019 的安装包自带 Unity 编辑器的安装入口。在你的开始菜单中搜索并打开“Visual Studio Installer”。如果你没有安装 Visual Studio 的话，那么还是建议去下载安装一下的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-10-43-44.png&quot; alt=&quot;Visual Studio Installer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;启动完 Visual Studio Installer 之后，选择“修改”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-10-48-28.png&quot; alt=&quot;修改 Visual Studio Installer 的组件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在“单个组件”里面勾选两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Unity 64 位编辑器&lt;/li&gt;
  &lt;li&gt;Visual Studio Tools for Unity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-10-51-08.png&quot; alt=&quot;勾选 Unity 组件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击右下角的“修改”后就等待。（可能部分运营商的速度会过慢，这时你可能需要考虑梯子。）&lt;/p&gt;

&lt;h3 id=&quot;安装-unity-hub&quot;&gt;安装 Unity Hub&lt;/h3&gt;

&lt;p&gt;下载安装地址：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://unity3d.com/get-unity/download&quot;&gt;Download - Unity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;获得授权&quot;&gt;获得授权&lt;/h2&gt;

&lt;p&gt;如果没有 Unity 的授权，那么你将无法使用 Unity 编辑器，而 Unity 的授权在 Unity Hub 的应用中才能进行（这也是为什么一定要下一个 Unity Hub 的原因）。&lt;/p&gt;

&lt;p&gt;启动 Unity Hub。如果你没有许可证的话，那么打开 Unity Hub 的第一个界面就是 Unity Hub 的许可证的授权界面。如果没有打开这个界面，那么点击右上角的设置-&amp;gt;许可证管理可以进来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-18-09.png&quot; alt=&quot;激活许可证&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“激活新许可证”，然后按照你自己的需要选择许可证即可。我出于个人学习 Unity 的需要安装的 Unity，所以选择了个人授权。个人授权免费，不过有效期只有一天，过期需要手工再操作一次。&lt;/p&gt;

&lt;h2 id=&quot;配置-visual-studio-集成&quot;&gt;配置 Visual Studio 集成&lt;/h2&gt;

&lt;h3 id=&quot;使用-visual-studio-解决方案&quot;&gt;使用 Visual Studio 解决方案&lt;/h3&gt;

&lt;p&gt;在 Unity Hub 中新建一个项目，输入名称选择路径，你就可以开始使用 Unity 来制作你的程序了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-20-53.png&quot; alt=&quot;新建项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;新建或打开 Unity 的项目后，会启动 Visual Studio 2019 安装过程中安装的那个 Unity 编辑器。&lt;/p&gt;

&lt;p&gt;选择“Edit-&amp;gt;Preference…”打开 Unity 的设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-24-34.png&quot; alt=&quot;Preference&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在设置中，打开 External Tools，然后在 External ScriptEditor 中选择“Browse…”，找到 Visual Studio 2019 的主程序确定，这时，这里就会变成“Visual Studio 2019”。下面是否勾选 Generate all .csproj files 的区别是生成的 Visual Studio 解决方案中是否包含其他所有的项目（后面会介绍）。&lt;/p&gt;

&lt;p&gt;Visual Studio 一般在这种地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-26-06.png&quot; alt=&quot;设置外部工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;设置完成之后，点击“打开 C# 项目”可以在 Visual Studio 中打开此项目的解决方案，你就可以在里面编写 C# 脚本了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-38-09.png&quot; alt=&quot;打开 C# 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;后话，其实你什么都不配也依然能使用 Visual Studio 完成开发，不过配完后你将获得这些好处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;获得完整的 Visual Studio 项目，可以用 C#/.NET 的语法分析，可以管理项目（否则你只能以单纯的 C# 单个文件编辑代码）；&lt;/li&gt;
  &lt;li&gt;可以直接在 Visual Studio 中调试 Unity 程序，获得比较完整的 Visual Studio 的调试体验。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你在前面勾选了“生成所有的 .csproj 文件”，那么在 Visual Studio 的解决方案中将可以看到所有的 Unity 辅助项目可供编辑。否则只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly-CSharp&lt;/code&gt; 一个项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-32-36.png&quot; alt=&quot;所有的 Unity 项目&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在-visual-studio-中调试-unity-程序&quot;&gt;在 Visual Studio 中调试 Unity 程序&lt;/h3&gt;

&lt;p&gt;正常你可以直接在项目原本的“启动”或“调试”按钮处看到“附加到 Unity”按钮，点击即可调试 Unity 程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-35-57.png&quot; alt=&quot;附加到 Unity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，Unity 编辑器这边也要运行起来才可以在 Visual Studio 里面进入断点：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-40-20.png&quot; alt=&quot;需要运行 Unity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你没有找到“附加到 Unity”按钮，那么可以在 Visual Studio 的“调试”菜单中找到“附加 Unity 调试程序”。点击后可以自动查找当前正在运行的 Unity 编辑器，选择你希望调试的那一个即可开始调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-42-15.png&quot; alt=&quot;附加 Unity 调试程序&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装其他模块语言编译平台&quot;&gt;安装其他模块（语言/编译平台）&lt;/h2&gt;

&lt;p&gt;实际上，按照以上步骤全部完成的话，编译和调试 Unity 项目已经没有问题了。如果你是出于学习目的安装的话，本文的操作到此结束。&lt;/p&gt;

&lt;p&gt;至此我们的 Unity 项目并不能发布，因为我们没有安装过任何目标平台的编译环境。&lt;/p&gt;

&lt;p&gt;请前往 Unity 的更新页面：&lt;a href=&quot;https://unity3d.com/get-unity/update&quot;&gt;https://unity3d.com/get-unity/update&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;因为我们已经安装了 Unity Hub，所以可以选择：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I already have Unity Hub&lt;br /&gt;
Install version from Unity Hub&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这时会启动 Unity Hub 然后进入更新页面。如果打开了 Unity Hub 但没有进入更新页面，请去任务栏通知区域右键退出 Unity Hub，重新点击上面的那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Install version from Unity Hub&lt;/code&gt; 按钮。于是可以选择安装哪些组件。&lt;/p&gt;

&lt;p&gt;随后点击右下角的“安装”以安装新版本并包含需要的编译组件。&lt;/p&gt;

&lt;p&gt;当然，如果你已经安装过新版本，那么也可以通过“添加组件”的方式来安装目标平台的编译环境。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-15-21-21.png&quot; alt=&quot;添加模块&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-05-05-15-22-35.png&quot; alt=&quot;可添加的模块&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;完成&quot;&gt;完成&lt;/h2&gt;

&lt;p&gt;至此，Unity 的安装和基本配置已全部完成。&lt;/p&gt;
</description>
        <pubDate>Tue, 05 May 2020 07:22:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-install-and-integrated-with-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-install-and-integrated-with-visual-studio.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>.NET 程序如何获取图片的宽高（框架自带多种方法的不同性能）</title>
        <description>&lt;p&gt;获取图片宽高的方法有很多种，本文介绍 .NET 中获取图片宽高的几种方法并评估其性能。如果你打算对大量图片进行一些处理，本文可能有用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;本文即将评估的方法&quot;&gt;本文即将评估的方法&lt;/h2&gt;

&lt;p&gt;本文即将采用以下四种方法获取图片：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing.Imaging.Metafile&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing.Bitmap&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapImage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapDecoder&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;systemdrawingimagingmetafile&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing.Imaging.Metafile&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;实际上不要被这个名字误解了，&lt;code class=&quot;highlighter-rouge&quot;&gt;Metafile&lt;/code&gt; 并不是“某个图片的元数据”，与之对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;MetafileHeader&lt;/code&gt; 也不是“某个图片的元数据头”。Metafile 是微软 Windows 系统一种图片格式，也就是大家熟悉的 wmf 和 emf，分别是 Windows Metafile 和 Enhanced Metafile。&lt;/p&gt;

&lt;p&gt;所以指望直接读取图片元数据头来提升性能的的小伙伴们注意啦，这不是你们要找的方法。&lt;/p&gt;

&lt;p&gt;不过为什么这个也能拿出来说，是因为此类也可以读取其他格式的图片。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Metafile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\blog.walterlv.com\large-background-image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;witdh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;能拿到。&lt;/p&gt;

&lt;h3 id=&quot;systemdrawingbitmap&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Drawing.Bitmap&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;这个实际上是封装的 GDI+ 位图，所以其性能最好也是 GDI+ 的性能，然而都知道 GDI+ 的静态图片性能不错，但比起现代的其他框架来说确实差得多。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\blog.walterlv.com\large-background-image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;witdh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;systemwindowsmediaimagingbitmapimage&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapImage&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;这是 WPF 框架中提供的显示位图的方法，生成的图片可以直接被 WPF 框架显示。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\blog.walterlv.com\large-background-image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;witdh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;systemwindowsmediaimagingbitmapdecoder&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapDecoder&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;这也是 WPF 框架中提供的方法，但相比完全加载图片到可以显示的 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapImage&lt;/code&gt;，此方法的性能会好得多。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JpegBitmapDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\blog.walterlv.com\large-background-image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapCreateOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DelayCreation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapCacheOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnDemand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;witdh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;性能对比&quot;&gt;性能对比&lt;/h2&gt;

&lt;p&gt;为了测试性能，我使用下面这张非常大的图，同一张图运行多次：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-14-large-background-image.jpg&quot; alt=&quot;大图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分别运行以上四个方法各 1 次：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-07-48-35.png&quot; alt=&quot;运行 1 次的时间消耗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分别运行以上四个方法各 10 次：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-07-50-48.png&quot; alt=&quot;运行 10 次的时间消耗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分别运行以上四个方法各 100 次（可以发现大量的 GC）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-07-52-10.png&quot; alt=&quot;运行 100 次的时间消耗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，使用不同的图片运行多次。&lt;/p&gt;

&lt;p&gt;分别运行以上四个方法各 10 张图片：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-08-05-52.png&quot; alt=&quot;运行 10 次的时间消耗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分别运行以上四个方法各 100 张图片（可以发现大量的 GC）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-08-03-21.png&quot; alt=&quot;运行 100 次的时间消耗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;做成图表，对于同一张图片运行不同次数：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;消耗时间(ms)&lt;/th&gt;
      &lt;th&gt;Metafile&lt;/th&gt;
      &lt;th&gt;Bitmap&lt;/th&gt;
      &lt;th&gt;BitmapImage&lt;/th&gt;
      &lt;th&gt;BitmapDecoder&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1次&lt;/td&gt;
      &lt;td&gt;175&lt;/td&gt;
      &lt;td&gt;107&lt;/td&gt;
      &lt;td&gt;71&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10次&lt;/td&gt;
      &lt;td&gt;1041&lt;/td&gt;
      &lt;td&gt;1046&lt;/td&gt;
      &lt;td&gt;63&lt;/td&gt;
      &lt;td&gt;17&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;100次&lt;/td&gt;
      &lt;td&gt;10335&lt;/td&gt;
      &lt;td&gt;10360&lt;/td&gt;
      &lt;td&gt;56&lt;/td&gt;
      &lt;td&gt;122&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-08-28-44.png&quot; alt=&quot;同一张图运行不同次数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于不同图片运行不同次数：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;消耗时间(ms)&lt;/th&gt;
      &lt;th&gt;Metafile&lt;/th&gt;
      &lt;th&gt;Bitmap&lt;/th&gt;
      &lt;th&gt;BitmapImage&lt;/th&gt;
      &lt;th&gt;BitmapDecoder&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1次&lt;/td&gt;
      &lt;td&gt;175&lt;/td&gt;
      &lt;td&gt;107&lt;/td&gt;
      &lt;td&gt;71&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10次&lt;/td&gt;
      &lt;td&gt;998&lt;/td&gt;
      &lt;td&gt;980&lt;/td&gt;
      &lt;td&gt;83&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;100次&lt;/td&gt;
      &lt;td&gt;10582&lt;/td&gt;
      &lt;td&gt;10617&lt;/td&gt;
      &lt;td&gt;255&lt;/td&gt;
      &lt;td&gt;204&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1000次&lt;/td&gt;
      &lt;td&gt;127023&lt;/td&gt;
      &lt;td&gt;128627&lt;/td&gt;
      &lt;td&gt;3456&lt;/td&gt;
      &lt;td&gt;4015&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-17-08-29-42.png&quot; alt=&quot;不同图片运行不同次数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以发现，对于 .NET 框架中原生自带的获取图片尺寸的方法来说：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapDecoder&lt;/code&gt; 的整体性能是最好的&lt;/li&gt;
  &lt;li&gt;对于同一张图，&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Media.Imaging.BitmapImage&lt;/code&gt; 的运行时间不随次数的增加而增加，其内部有缓存&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/WMF&quot;&gt;WMF - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 10:35:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-image-pixel-width-and-height-in-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-image-pixel-width-and-height-in-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>在 CMD 里根据进程名杀掉进程</title>
        <description>&lt;p&gt;任务管理器杀进程大家都会，不过如果你的系统被卡到任务管理器都无法操作了，怎么办？拿控制台 CMD 吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/process-by-name-using-cmd&quot;&gt;在 CMD 里根据进程名杀掉进程 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/process-by-name-using-powershell&quot;&gt;在 PowerShell 里根据进程名杀掉进程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;taskkill-命令&quot;&gt;taskkill 命令&lt;/h2&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;taskkill&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/im&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/f&lt;/code&gt; 表示强制终止进程。如果不指定，那么 taskkill 会给进程发送终止信号，但进程可以阻止退出（例如提示文档需要保存）。指定了，就会强杀进程。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/t&lt;/code&gt; 表示结束此进程和其子进程。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/im&lt;/code&gt; 用来指定进程的影映像名称（有 .exe 后缀）。&lt;/p&gt;

&lt;p&gt;更多使用方法可以直接输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;taskkill /?&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;❯&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;taskkill&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/P&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;processid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;imagename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;描述&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;使用该工具按照进程&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;或映像名称终止任务。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;参数列表&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定要连接的远程系统。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定应该在哪个用户上下文执行这个命令。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/P&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;为提供的用户上下文指定密码。如果忽略，提示&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输入。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;应用筛选器以选择一组任务。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;允许使用&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。例如，映像名称&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;acme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;processid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定要终止的进程的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PID&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;使用&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TaskList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;取得&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;PID&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;imagename&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定要终止的进程的映像名称。通配符&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可用来&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定所有任务或映像名称。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/T&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;终止指定的进程和由它启用的子进程。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定强制终止进程。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;显示帮助消息。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;筛选器&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;筛选器名&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;有效运算符&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;有效值&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;STATUS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RUNNING&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NOT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RESPONDING&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;UNKNOWN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;IMAGENAME&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;映像名称&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;值&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;SESSION&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;会话编号。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;CPUTIME&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;CPU&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;时间，格式为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;mm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;时，&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;mm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分，&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;秒&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MEMUSAGE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;内存使用量，单位为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用户名，格式为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;MODULES&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;DLL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;名称&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;SERVICES&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;服务名称&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;WINDOWTITLE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;窗口标题&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;说明&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;只有在应用筛选器的情况下，&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;切换才能使用通配符&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;远程进程总是要强行&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;终止。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;当指定远程机器时，不支持&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WINDOWTITLE&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;和&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;STATUS&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;筛选器。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;例如&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notepad.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1230&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1241&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/PID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1253&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/T&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cmd.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/T&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PID ge 1000&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WINDOWTITLE ne untitle*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;USERNAME eq NT AUTHORITY\SYSTEM&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notepad.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;域&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用户名&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;用户名 ne NT*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/IM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TASKKILL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/P&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/FI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;IMAGENAME eq note*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/29233238/6233938&quot;&gt;how to kill a program by process name using cmd in windows 8? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 03:31:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/kill-process-by-name-using-cmd.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/kill-process-by-name-using-cmd.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>在 PowerShell 里根据进程名杀掉进程</title>
        <description>&lt;p&gt;任务管理器杀进程大家都会，不过如果你的系统被卡到任务管理器都无法操作了，怎么办？直接在 PowerShell 中干掉！另外，这也非常容易集成到各种工具链中。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/process-by-name-using-cmd&quot;&gt;在 CMD 里根据进程名杀掉进程 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/process-by-name-using-powershell&quot;&gt;在 PowerShell 里根据进程名杀掉进程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;stop-process--kill&quot;&gt;Stop-Process / kill&lt;/h2&gt;

&lt;p&gt;PowerShell 脚本 Stop-Process 可以用来结束进程。&lt;/p&gt;

&lt;p&gt;结束进程名为 chrome 的进程：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Stop-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# kill 是 Stop-Process 的简写，ProcessName 是 Name 的别名。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kill&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ProcessName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果除了关闭你正在看的博客，还顺便要把自己正在编写的代码关闭掉，可以传多个进程名：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Stop-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;taskkill-命令&quot;&gt;taskkill 命令&lt;/h2&gt;

&lt;p&gt;PowerShell 依然能使用 CMD 命令，于是以下命令依旧可以工作：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;taskkill&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/im&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/process-by-name-using-cmd&quot;&gt;在 CMD 里根据进程名杀掉进程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;其他间接玩法&quot;&gt;其他间接玩法&lt;/h2&gt;

&lt;h3 id=&quot;processkill&quot;&gt;Process.Kill&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Process&lt;/code&gt; 能拿到进程对象，于是可以利用管道拿到对象将其关闭：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Get-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更间接一点，遍历所有拿到的 Process 对象，然后杀掉：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Get-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Foreach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Kill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 03:31:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/kill-process-by-name-using-powershell.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/kill-process-by-name-using-powershell.html</guid>
        
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>Linux Shell 中需要转义的字符</title>
        <description>&lt;p&gt;本文整理 Linux Shell 中的转义字符。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Linux Shell 中，有很多字符是有特殊含义的，如果期望把这个字符当作普通字符来处理，需要经过 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 的转义。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;在双引号中即可变普通字符的特殊字符&quot;&gt;在双引号中即可变普通字符的特殊字符&lt;/h2&gt;

&lt;p&gt;` ` &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;空格--&quot;&gt;空格 ‘\ `&lt;/h3&gt;

&lt;p&gt;这是转义空格。如果路径中包含空格，那么使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 转义可以避免路径被分割成 Shell 的两个参数。&lt;/p&gt;

&lt;p&gt;我有另一篇描述 Linux Shell 中路径空格转义相关的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/typing-difference-among-shells-in-different-operating-systems.html&quot;&gt;了解 Windows/Linux 下命令行/Shell 启动程序传参的区别，这下不用再担心 Windows 下启动程序传参到 Linux 下挂掉了 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;星号-&quot;&gt;星号 ‘*`&lt;/h3&gt;

&lt;p&gt;如果单独使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 将会表示当前路径下枚举的所有文件或文件夹。如果希望保持 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 的原意，那么将其包裹在引号内，或者使用转义 &lt;code class=&quot;highlighter-rouge&quot;&gt;\*&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;井号-&quot;&gt;井号 &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;表示注释。&lt;/p&gt;

&lt;h3 id=&quot;换行符&quot;&gt;换行符&lt;/h3&gt;

&lt;p&gt;在引号中，也可以直接换行。这样换行符就是字符串的一部分。&lt;/p&gt;

&lt;h2 id=&quot;即便在引号中也依然被-shell-解释的特殊字符&quot;&gt;即便在引号中也依然被 Shell 解释的特殊字符&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;双引号-&quot;&gt;双引号 ‘&quot;’&lt;/h3&gt;

&lt;p&gt;双引号的作用是避免空格将本来属于同一段参数的字符串分割成两部分。那么如果真的需要双引号的话就需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 来转义。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样才可以输出：&lt;code class=&quot;highlighter-rouge&quot;&gt;Hello &quot;Walterlv&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;反引号-&quot;&gt;反引号 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;跟引号一样的作用。&lt;/p&gt;

&lt;p&gt;在引号中也需要转义。&lt;/p&gt;

&lt;h3 id=&quot;美元符-&quot;&gt;美元符 &lt;code class=&quot;highlighter-rouge&quot;&gt;\$&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在 Linux Shell 中，这是变量的引用。例如 ${x} 就是引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; 变量。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;上一个程序的返回值为：&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;? = &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
上一个程序的返回值为：&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 127
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在引号中也需要转义。&lt;/p&gt;

&lt;h3 id=&quot;反斜杠-&quot;&gt;反斜杠 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是转义字符，所以其本身的使用也必然需要转义。&lt;/p&gt;

&lt;p&gt;在引号中也需要转义。&lt;/p&gt;

&lt;h2 id=&quot;任意字符&quot;&gt;任意字符&lt;/h2&gt;

&lt;p&gt;任意字符也可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 转义，虽然没用，但也是一个特性。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\H\e\l\l\o\ \&quot;\W\a\l\t\e\r\l\v\&quot;&lt;/span&gt;
Hello &lt;span class=&quot;s2&quot;&gt;&quot;Walterlv&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.shellscript.sh/escape.html&quot;&gt;Escape Characters - Shell Scripting Tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/9734212/6233938&quot;&gt;How to enable linux support double backslashes “\” as the path delimiter - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unix.stackexchange.com/questions/484197/backslash-in-path&quot;&gt;shell - Backslash in Path - Unix &amp;amp; Linux Stack Exchange&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/20053121/6233938&quot;&gt;shell - Which characters need to be escaped when using Bash? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 03:12:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/linux-shell-escape.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/linux-shell-escape.html</guid>
        
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>了解 Windows/Linux 下命令行/Shell 启动程序传参的区别，这下不用再担心 Windows 下启动程序传参到 Linux 下挂掉了</title>
        <description>&lt;p&gt;启动某个程序，再带上一堆参数，这几乎是程序员们每天必做到事情。另外再算上各种辅助程序员们的自动化脚本，辅助构建的 CI（持续集成）等等，程序员们在创造大量的应用程序然后调用它们。&lt;/p&gt;

&lt;p&gt;但是，不经常跨系统玩这些的小伙伴们注意了，Windows 下的 Shell 和 Linux 下的 Shell 是有区别的！如果你不了解这些区别，很容易造成在 Windows 下编写的代码/脚本在 Linux 下无法使用的问题。&lt;/p&gt;

&lt;p&gt;本文列举 Windows/Linux 下 Shell 的区别。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;分号&quot;&gt;分号（;）&lt;/h2&gt;

&lt;p&gt;分号（;）在 Linux 的 Shell 中是不同命令的分割，而在 Windows 中只是一个普通的字符。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;dotnet pack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这在 Linux 中是执行两句不同的命令，&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet pack&lt;/code&gt;。而换到 Windows 中，这变成了执行 dotnet 程序，然后传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;build;dotnet pack&lt;/code&gt; 这个参数。&lt;/p&gt;

&lt;p&gt;相反的：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NET48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NETCOREAPP3_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RELEASE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这在 Windows 下是启动 foo 程序，然后传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;NET48;NETCOREAPP3_1;RELEASE&lt;/code&gt;，而在 Linux 下则变成了执行三个不同的命令。后面两个显然不是命令，于是执行时会报 127 错误：Command not found。（程序执行完成退出，返回值为 127。）&lt;/p&gt;

&lt;p&gt;如果你希望你的执行脚本跨平台，那么：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不要使用分号 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 来尝试将两个或多个不同的命令合并成 1 行，直接执行多个命令即可。&lt;/li&gt;
  &lt;li&gt;如果命令名称或参数中存在分号，则必须使用引号 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; 将它包裹起来。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;路径空格&quot;&gt;路径空格&lt;/h2&gt;

&lt;p&gt;Windows 下针对路径中包含空格的情况，用引号包裹路径：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\Walterlv\Foo.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Linux 下，如果路径中包含空格，则有三种不同的解决策略：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 加 \ 转义&lt;/span&gt;
/mnt/c/Program&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Files/Walterlv/Foo

&lt;span class=&quot;c&quot;&gt;# 加双引号&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;/mnt/c/Program Files/Walterlv/Foo&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 加单引号&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;'/mnt/c/Program Files/Walterlv/Foo'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以发现，两者都有的方案是加双引号。所以，如果希望你的命令脚本跨平台使用，则应该使用双引号包裹路径。&lt;/p&gt;

&lt;h2 id=&quot;路径分隔符&quot;&gt;路径分隔符&lt;/h2&gt;

&lt;p&gt;Windows 下，&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 都是路径分隔符。Linux 下，只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 是路径分隔符，&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是合理的文件名，在 Shell 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是转义字符。&lt;/p&gt;

&lt;p&gt;虽然理论上所有路径都使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 可以让你的跨平台脚本在以上所有系统中正常工作，但考虑到 Windows 可能有一些逗比程序对 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 支持不好，更建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在所有场景下生成路径字符串时使用当前平台的路径分隔符&lt;/li&gt;
  &lt;li&gt;不要将某平台生成的路径分隔符直接拿到另一平台使用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于跨平台路径分隔符的问题，我专门写了一篇博客，在那里可以了解更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/format-mixed-path-seperators-to-platform-special.html&quot;&gt;.NET 将混合了多个不同平台（Windows / Mac / Linux）的文件/目录的路径格式化成同一个平台下的路径 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;其他特殊字符------&quot;&gt;其他特殊字符（ &lt;code class=&quot;highlighter-rouge&quot;&gt;(&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;{&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; ）&lt;/h2&gt;

&lt;p&gt;在 Linux 的 Shell 中，有很多字符有特殊用途，而在 Windows Shell 中，这些字符的作用完全由被调用的应用程序来决定。&lt;/p&gt;

&lt;p&gt;所以对于跨平台的脚本，最好尽量避免使用这些字符。&lt;/p&gt;

&lt;p&gt;关于 Linux 下这些转义字符的用途，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/linux-shell-escape.html&quot;&gt;Linux Shell 中的所有需要转义的字符 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 01:54:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/typing-difference-among-shells-in-different-operating-systems.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/typing-difference-among-shells-in-different-operating-systems.html</guid>
        
        
        <category>windows</category>
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>git 乱改你的换行符？一句话设置让 git 不再碰你某个文件的换行符</title>
        <description>&lt;p&gt;前些天有位小伙伴告诉我说 git 改了某个重要文件的换行符，导致文件的哈希变了，于是文件校验出现错误。之前一直没问题而最近才有问题是因为最近换了部署服务器，git 的换行符配置不一样。&lt;/p&gt;

&lt;p&gt;其实，我们不应该让代码仓库如此容易受到外界环境的影响。所以本文会解释 git 的全局配置如何影响了 git 对换行符的处理，然后说说如何彻底解决这个问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于换行符&quot;&gt;关于换行符&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;\r = CR = Carriage-Return = 回车&lt;/li&gt;
  &lt;li&gt;\n = LF = Line-Feed = 换行&lt;/li&gt;
  &lt;li&gt;\r\n = CRLF = Carriage-Return Line-Feed = 回车换行&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Windows 下默认的文本换行符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt;，Linux 下默认的换行符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\n&lt;/code&gt;，Mac 下默认的换行符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r&lt;/code&gt;。因为这些差异，如果某部分文本文件会跨操作系统处理，那么换行符的处理就必须考虑了。git 允许开发者设置如何处理换行符在跨平台上的处理方式，不过不合适的设置可能带来文件发生不期望的修改。&lt;/p&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;问题本身在本文一开始已经说得比较清楚了，现在疏理一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有个文本文件，被 git 改了换行符，导致哈希变化，文件校验出现了错误；&lt;/li&gt;
  &lt;li&gt;部署服务器以前 git 全局配置和现在不同，所以以前没问题，现在出了问题。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;当时，&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 小伙伴是负责部署服务器配置的，看到出事了立刻想到去服务器把配置改“正确”。&lt;/p&gt;

&lt;p&gt;然而我阻止了。因为现在因为换服务器出问题，将来也会因为换服务器出问题，更普遍的，换任何环境都可能出问题。所以这问题应该从仓库着手，避免此文件被修改换行符。&lt;/p&gt;

&lt;p&gt;于是我和小伙伴结对打开了 .gitattribute 文件，在末尾加了一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    *.bmp       binary
    *.jpg       binary
&lt;span class=&quot;gi&quot;&gt;++  *.inf       binary
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，*.inf 文件会被 git 视为二进制文件，也就不会处理换行符了。&lt;/p&gt;

&lt;p&gt;当然，因为项目很小，所以直接改了位于项目根目录的 .gitattribute 文件。如果项目比较大，那么建议考虑在那个 .inf 文件所在的文件夹新建一个 .gitignore 文件，避免全局的设置对可能不需要生效的文件也起了作用。&lt;/p&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;git 有个全局配置，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;%USERPROFILE%\.gitconfig&lt;/code&gt; 文件里面，可以指定如何处理文本文件的换行符：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[core]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;autocrlf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有三个可选值：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;true&lt;/li&gt;
  &lt;li&gt;false&lt;/li&gt;
  &lt;li&gt;input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 Windows 系统上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表示在推送时转成 &lt;code class=&quot;highlighter-rouge&quot;&gt;\n&lt;/code&gt;，在拉取时转成 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt;。这样的设置让 Windows 的开发者能兼容很多的开发工具（比如早期的记事本，新的已经支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt; 了），不至于遇到很多换行符问题。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 表示在推送时和拉取时都原样保留换行符。这样的设置在所有程序员都在同一个平台开发时很有用，git 完全不处理换行符，全部改由开发者自行解决。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;input&lt;/code&gt; 表示在推送时转成 &lt;code class=&quot;highlighter-rouge&quot;&gt;\n&lt;/code&gt;，在拉取时原样保留换行符。注意到，这样的设置会让仓库里所有的换行符都变成 &lt;code class=&quot;highlighter-rouge&quot;&gt;\n&lt;/code&gt; 不再有什么时候有 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt; 了，所以对 Windows 平台的开发者并不友好。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以前的服务器全局配置没有问题，是因为服务器配置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，于是拉下来时一定都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt; 哈希正确。而现在全局配置是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，于是会原样把 git 仓库里的拉下来，哈希错误。&lt;/p&gt;

&lt;p&gt;是的，你没看错！远程 git 仓库里的是错的！这是因为有小伙伴使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;input&lt;/code&gt; 的配置，导致推送时统一把换行符改成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;\r\n&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 01:26:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-git-unexpected-line-end-changing.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-git-unexpected-line-end-changing.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET 将混合了多个不同平台（Windows / Mac / Linux）的文件/目录的路径格式化成同一个平台下的路径</title>
        <description>&lt;p&gt;Windows 下的路径分隔符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 而 Linux 和 Mac 下的路径分隔符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;。正常如果你的数据不跨 Windows 和 Linux 平台流通的话，不怎么会遇到多种换行符并存的问题的。但如果真发生了流通，那么如何将它们格式化为统一的当前平台认识的分隔符呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;现有方案&quot;&gt;现有方案&lt;/h2&gt;

&lt;h3 id=&quot;没有原生方案net&quot;&gt;没有原生方案（.NET）&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.IO.Path&lt;/code&gt; 带了一堆方法用来处理路径。各大文档博客和书籍也都推荐大家使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 来处理路径字符串的拼接、拆分和提取等，这可以很大程度避免不同遭遇不同平台下路径分隔字符串不一致导致的各种问题。&lt;/p&gt;

&lt;p&gt;不过，本文想告诉大家的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 处理路径字符串也不是万能的，这体现在处理跨操作系统的路径字符串时。&lt;/p&gt;

&lt;p&gt;现在，我列举了 6 个不同的路径字符串：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;/mnt/d/walterlv/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\walterlv\&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;Foo/Bar.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;Foo\Bar.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;/mnt/d/walterlv/Foo/Bar.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\walterlv\Foo\Bar.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分成三组。前两个是路径的前半部分，中间两个是路径的后半部分，最后两个是完整路径。每组里面，前者是 Linux 风格的路径分隔符，后者是 Windows 风格的路径分隔符。&lt;/p&gt;

&lt;p&gt;现在，我将试图将以下几种混合情况下的路径拼接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 可能格式化的方法输出出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 看看 Linux 风格和 Windows 风格直接拼接的换行符使用 Path.Combine 能否格式化成功。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromCombine0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromCombine1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.Combine(part0, part3) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromCombine0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.Combine(part1, part2) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromCombine0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 通过 Path.GetFullPath 转相对路径到完整路径时，看看能否将路径格式化成当前平台。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFull0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFull1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.GetFullPath(part2) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFull0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.GetFullPath(part3) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFull1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 通过 new FileInfo(file).FullName 的一层转换看看能否将混合路径格式化成当前平台。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFileInfo0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromCombine0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFileInfo1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromCombine1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;FileInfo(part0 + part3).FullName = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFileInfo0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;FileInfo(part1 + part2).FullName = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFileInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 通过 new FileInfo(file).FullName 的一层转换看看能否将非当前平台的路径格式化成当前平台。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFileInfo2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathFromFileInfo3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.GetFullPath(part4) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFileInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Path.GetFullPath(part5) = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathFromFileInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;猜猜以上代码在 Windows 和 Linux 平台会输出什么？&lt;/p&gt;

&lt;p&gt;看图！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-29-20-54-33.png&quot; alt=&quot;Windows 和 Linux 平台下的输出&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图是拼接的，上面一半是 Windows 平台下的运行结果，下面一半是 Linux Ubuntu 18.04 发行版的运行结果。运行时是 .NET Core 3.1。&lt;/p&gt;

&lt;p&gt;可以发现这些点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Path.Combine&lt;/code&gt; 的路径拼接仅决定如何合并两段字符串，不会将已有的路径格式化成当前平台的路径分隔符。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetFullPath&lt;/code&gt; 在生成完整路径的时候，虽然补全的部分是当前平台的，但已有的部分依然是原本字符串。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;new FileInfo().FullName&lt;/code&gt; 在 Windows 平台下可以完美将路径字符串统一成 Windows 平台的风格；但在 Linux 平台上不会统一，已有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 不会变成 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;；无论是拼接的字符串，还是原本别的平台的字符串，都是一样的结论。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;为什么-net-原生不做统一化&quot;&gt;为什么 .NET 原生不做统一化？&lt;/h3&gt;

&lt;p&gt;看前面结论可知，在 Windows 平台下是可以将 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 全部格式化成 Windows 平台的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 的，但 Linux 下却不行。&lt;/p&gt;

&lt;p&gt;这并不是因为 .NET 没去做，而是无法做！&lt;/p&gt;

&lt;p&gt;在 Linux 下，&lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是合理的文件名&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;另外，路径经常使用在 Shell 中，而&lt;strong&gt;在 Shell 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是个转义字符&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;例如，你可以有一个文件，名字是 &lt;code class=&quot;highlighter-rouge&quot;&gt;foo\bar.txt&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以，.NET 绝对不能擅自给你将 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 当作路径分隔符进行格式化！&lt;/p&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 在 Linux Shell 中的转义，你可以阅读我的另外两篇博客了解：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/typing-difference-among-shells-in-different-operating-systems.html&quot;&gt;了解 Windows/Linux 下命令行/Shell 启动程序传参的区别，这下不用再担心 Windows 下启动程序传参到 Linux 下挂掉了 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;自己实现&quot;&gt;自己实现&lt;/h2&gt;

&lt;p&gt;知道了 Linux 是合理的文件名后，当然不能再指望有某个通用的解决方法了。因为通用代码不可能知道在你的上下文下，&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 是否是合理的文件名。在信息不足的情况下，前面 .NET 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;new FileInfo().FullName&lt;/code&gt; 已经是最好的解决方案了。&lt;/p&gt;

&lt;p&gt;所以，如果你明确这些不同种类的路径字符串的来源你都清楚（没错，就是你自己挖出来的坑），拼接出来之后的后果你才能知道是否是符合业务的。这时你才应该决定是否真的要做路径的格式化。&lt;/p&gt;

&lt;h3 id=&quot;简单省事型&quot;&gt;简单省事型&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'\\'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;高性能型&quot;&gt;高性能型&lt;/h3&gt;

&lt;p&gt;自己实现去。&lt;/p&gt;

&lt;h2 id=&quot;如何避免&quot;&gt;如何避免&lt;/h2&gt;

&lt;p&gt;从前面的分析可以知道，如果每个框架、库还有业务开发者都不去作死把平台特定的路径传递到其他平台，那么根本就不会存在不同平台的路径会拼接的情况。&lt;/p&gt;

&lt;p&gt;另外，开发者也不应该随便在代码中写死 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;\\&lt;/code&gt; 作为路径的分隔符。&lt;/p&gt;

&lt;p&gt;就这样……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/9734782/6233938&quot;&gt;How to enable linux support double backslashes “\” as the path delimiter - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Apr 2020 01:24:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/format-mixed-path-seperators-to-platform-special.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/format-mixed-path-seperators-to-platform-special.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>如何在 Windows 10 中安装 WSL2 的 Linux 子系统</title>
        <description>&lt;p&gt;本文介绍如何在 Windows 10 中安装 WSL2 的 Linux 子系统&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步启用虚拟机平台和-linux-子系统功能&quot;&gt;第一步：启用虚拟机平台和 Linux 子系统功能&lt;/h2&gt;

&lt;p&gt;以管理员权限启动 PowerShell，然后输入以下命令启用虚拟机平台：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Enable-WindowsOptionalFeature&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Online&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FeatureName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualMachinePlatform&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以管理员权限启动 PowerShell，然后输入以下命令启用 Linux 子系统功能：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Enable-WindowsOptionalFeature&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Online&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FeatureName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Microsoft-Windows-Subsystem-Linux&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在以上每一步命令执行完之后，PowerShell 中可能会提示你重新启动计算机。按“Y”可以重新启动。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-22-01.png&quot; alt=&quot;启用 VirtualMachinePlatform&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-25-26.png&quot; alt=&quot;启用 Microsoft-Windows-Subsystem-Linux&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-26-11.png&quot; alt=&quot;正在启用 Linux 子系统&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，这个命令跟你在控制面板中启用“适用于 Windows 的 Linux 子系统”功能是一样的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-53-22.png&quot; alt=&quot;在控制面板中启用虚拟机平台和 Linux 子系统&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步安装一个-linux-发行版&quot;&gt;第二步：安装一个 Linux 发行版&lt;/h2&gt;

&lt;p&gt;打开微软商店应用，在搜索框中输入“Linux”然后搜索，你可以看到搜索结果中有很多的 Linux 发行版可以选择。选择一个你喜欢的 Linux 发行版本然后安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-30-08.png&quot; alt=&quot;搜索 Linux&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选择一个 Linux 发行版本然后安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-08-31-34.png&quot; alt=&quot;安装一个 Linux 发行版&quot; /&gt;&lt;/p&gt;

&lt;p&gt;需要注意，在商店中的安装并没有实际上完成 Linux 子系统的安装，你还需要运行一次已安装的 Linux 发行版以执行真正的安装操作。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-09-26-03.png&quot; alt=&quot;安装 Linux&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步启用-wsl2&quot;&gt;第三步：启用 WSL2&lt;/h2&gt;

&lt;p&gt;重要：&lt;strong&gt;你的操作系统版本必须至少大于或等于 Windows 10.0.18917 ！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;wsl -l&lt;/code&gt; 可以列出当前系统上已经安装的 Linux 子系统名称。注意这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;-l&lt;/code&gt; 是列表“list”的缩写，是字母 &lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt; 不是其他字符。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;wsl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果提示 &lt;code class=&quot;highlighter-rouge&quot;&gt;wsl&lt;/code&gt; 不是内部或外部命令，说明你没有启用“适用于 Windows 的 Linux 子系统”，请先完成本文第一步。&lt;/p&gt;

&lt;p&gt;如果提示没有发现任何已安装的 Linux，说明你没有安装 Linux 发行版，或者只是去商店下载了，没有运行它执行真正的安装，请先完成本文第二步。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;wsl --set-version &amp;lt;Distro&amp;gt; 2&lt;/code&gt; 命令可以设置一个 Linux 发行版的 WSL 版本。命令中 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Distro&amp;gt;&lt;/code&gt; 替换为你安装的 Linux 发型版本的名称，也就是前面通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;wsl -l&lt;/code&gt; 查询到的名称。&lt;/p&gt;

&lt;p&gt;本文的示例使用的是小白门喜欢的 Ubuntu 发行版。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;wsl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--set-version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ubuntu&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-10-12-35.png&quot; alt=&quot;设置 WSL2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，使用以下命令可以在以后安装 Linux 的时候默认启用 WSL2：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;wsl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--set-default-version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/wsl/wsl2-install&quot;&gt;Install WSL 2 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 28 Apr 2020 12:26:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-install-wsl2.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-install-wsl2.html</guid>
        
        
        <category>windows</category>
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：在 Visual Studio 里使用 Visual Studio Tools for Unity 全套工具</title>
        <description>&lt;p&gt;Visual Studio 安装过程中一起勾选的 Visual Studio Tools for Unity 提供了与 Unity 编辑器方便的交互功能，充分使用 Visual Studio Tools for Unity 可以提升一部分开发效率减少一点点坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;visual-studio-tools-for-unity&quot;&gt;Visual Studio Tools for Unity&lt;/h2&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/unity-starter-install-and-integrated-with-visual-studio.html&quot;&gt;Unity3D 入门：安装 Unity3D 并配置与 Visual Studio 的协作开发环境 - walterlv&lt;/a&gt; 一文中提及了在安装 Unity 的开发环境时建议勾选了 Visual Studio Tools for Unity。&lt;/p&gt;

&lt;p&gt;如果你还没安装，可以阅读此博客安装。如果安装后没有设置 Unity 编辑器的关联，也可以阅读这篇博客了解如何设置关联。&lt;/p&gt;

&lt;h2 id=&quot;快速实现-unity-消息&quot;&gt;快速实现 Unity 消息&lt;/h2&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MonoBehaviour&lt;/code&gt; 的类中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;onXXX&lt;/code&gt; 可以在智能感知列表中看到 Unity 在游戏运行时给每个游戏对象广播的消息，直接回车输入可以插入这个方法。于是，你可以无需记忆所有的这些消息就可以在不同的消息中添加处理函数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-12-32.png&quot; alt=&quot;onXXX&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-16-18.png&quot; alt=&quot;插入的处理函数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或者，你也可以在类中按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl&lt;/code&gt;+&lt;code class=&quot;highlighter-rouge&quot;&gt;Shift&lt;/code&gt;+&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; 打开“实现 Unity 消息”对话框，通过勾选插入一堆处理函数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-19-06.png&quot; alt=&quot;实现 Unity 消息&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;unity-项目资源管理器&quot;&gt;Unity 项目资源管理器&lt;/h2&gt;

&lt;p&gt;我们在 Unity 编辑器中查看 Unity 项目的文件结构与 Visual Studio 解决方案资源管理器中看到的是完全不同的。实际上，Visual Studio 中的项目和解决方案对 Unity 资产来说是没有意义的，有用的其实是里面的 C# 脚本。&lt;/p&gt;

&lt;p&gt;于是就有了“Unity 项目资源管理器”的需要，它可以以跟 Unity 编辑器相同的视角看 Unity 项目中的资产。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-24-55.png&quot; alt=&quot;打开 Unity 项目资源管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-29-05.png&quot; alt=&quot;在 Unity 项目资源管理器中查看&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-28-25.png&quot; alt=&quot;在 Unity 编辑器中查看&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;附加到-unity-调试&quot;&gt;附加到 Unity 调试&lt;/h2&gt;

&lt;p&gt;在安装了 Visual Studio Tools for Unity 后，打开 Unity 的项目你将看到平常的“调试”按钮变成了“附加到 Unity”按钮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-35-57.png&quot; alt=&quot;附加到 Unity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Unity 编辑器也运行起来的情况下，可以在 Visual Studio 里面进入断点调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-40-20.png&quot; alt=&quot;需要运行 Unity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，如果觉得每次都要单独去点“Play”比较麻烦的话，可以在调试按钮上下拉选择“附加到 Unity 并播放”。这样每次点击按钮的时候就直接会开始运行游戏了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-32-28.png&quot; alt=&quot;附加到 Unity 并播放&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你没有找到“附加到 Unity”按钮，那么可以在 Visual Studio 的“调试”菜单中找到“附加 Unity 调试程序”。点击后可以自动查找当前正在运行的 Unity 编辑器，选择你希望调试的那一个即可开始调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-42-15.png&quot; alt=&quot;附加 Unity 调试程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-33-54.png&quot; alt=&quot;选择 Unity 实例&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/cross-platform/getting-started-with-visual-studio-tools-for-unity?view=vs-2019&quot;&gt;Getting Started with Visual Studio Tools for Unity - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 27 Apr 2020 13:06:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-config-visual-studio-tools-for-unity.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-config-visual-studio-tools-for-unity.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Unity3D 入门：使用 Visual Studio 开发 Unity C# 脚本，说说根目录的那些 sln 和 csproj 文件</title>
        <description>&lt;p&gt;本文介绍 Unity3D 项目根目录的 sln 和 csproj 文件，你将知道如何正确理解和使用它们。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;对于传统 .NET/C# 的开发者来说，在解决方案中管理 NuGet 包，在 C# 项目中引用 dll 或 NuGet 包是家常便饭。但在 Unity 项目里面，你可能要改变这一观念——因为 Unity 项目里面实际上并不存在 sln 和 csproj 文件。&lt;/p&gt;

&lt;p&gt;等等！那我们在根目录看到的那些 sln 和 csproj 文件是什么？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-27-19-52-25.png&quot; alt=&quot;Unity 项目根目录下的 sln 和 csproj 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那只是 Unity 编辑器为了让你方便写 C# 代码临时生成给你用的。&lt;/p&gt;

&lt;p&gt;默认 Unity 不指定外部脚本编辑器时，会单纯打开 .cs 文件而已。而如果指定了 Visual Studio 作为外部脚本编辑器，那么再从 Unity 中打开 C# 项目时，将会生成 sln 和 csproj 文件，然后调用 Visual Studio 打开生成的 sln 和 csproj 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-38-09.png&quot; alt=&quot;打开 C# 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-26-11-26-06.png&quot; alt=&quot;设置外部工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里就需要特别注意了：&lt;strong&gt;每次点击 Open C# Project 打开 C# 项目时，都会重新生成 sln 和 csproj 文件&lt;/strong&gt;，所以实际上你对 sln 和 csproj 所做的任何改动都是无效的！&lt;/p&gt;

&lt;p&gt;这样的设计，有好处也有坏处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有了 sln 和 csproj，Visual Studio 将能充分运行代码分析器，可以在类与其他符号之间跳转，可以有智能感知提示，可以实时发现编写中的代码错误（甚至是引用错误）。&lt;/li&gt;
  &lt;li&gt;但让 Visual Studio 的各种功能激活后就会让我们这样的入门开发者产生误会，认为这其实就是 C# 项目，会尝试真的对这些项目进行可能超出 Unity 功能范围的修改。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;真正在编译完成放到游戏中运行的，是 Assets 文件夹中的文件。而外面的 sln 和 csproj 文件，应该加入到 .gitignore 文件中，从版本管理中忽略掉。&lt;/p&gt;
</description>
        <pubDate>Mon, 27 Apr 2020 13:04:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unity-starter-the-sln-and-csproj-files.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unity-starter-the-sln-and-csproj-files.html</guid>
        
        
        <category>unity</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>让你的程序置顶到比系统界面都更上层，就像任务管理器/放大镜一样绝对置顶</title>
        <description>&lt;p&gt;启动系统自带的放大镜程序，我们会发现即便进了 Windows 8 的开始屏幕，或打开了 Windows 10 的开始菜单和消息中心，它也依然显示在最顶层。如果你为任务管理器开启置顶效果，你会发现它也能显示到开始屏幕的顶层。这是怎么做到的呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;顺便解释下“桌面应用程序”，指的是传统 Win32 应用程序。解释下“Windows 应用”，指的是开始屏幕/开始菜单/UAP/UWP 甚至是锁屏界面这些。&lt;/p&gt;

&lt;h2 id=&quot;方法&quot;&gt;方法&lt;/h2&gt;

&lt;p&gt;做到这些，需要四个步骤，缺一不可：&lt;/p&gt;

&lt;h3 id=&quot;第一步修改-manifest&quot;&gt;第一步：修改 Manifest&lt;/h3&gt;

&lt;p&gt;前往你程序的 App.Manifest 文件，设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;requestedExecutionLevel&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;requestedExecutionLevel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;asInvoker&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uiAccess=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第二步修改窗口属性&quot;&gt;第二步：修改窗口属性&lt;/h3&gt;

&lt;p&gt;这两个属性是必须设置的，否则无法达到目的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ShowInTaskbar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TopMost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第三步为程序签名&quot;&gt;第三步：为程序签名&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/windows/2015/03/31/sign-for-desktop-application.html&quot;&gt;参见此处 - 为程序签名&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;第四步将程序放到受信任的目录下&quot;&gt;第四步：将程序放到受信任的目录下&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\program files
C:\program files x86
C:\Windows\system32
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;可能遇到的问题&quot;&gt;可能遇到的问题&lt;/h2&gt;

&lt;h3 id=&quot;从服务器返回了一个参照&quot;&gt;从服务器返回了一个参照&lt;/h3&gt;

&lt;p&gt;A referral was returned from the server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-25-11-48-26.png&quot; alt=&quot;从服务器返回了一个参照&quot; /&gt;&lt;/p&gt;

&lt;p&gt;感谢 &lt;a href=&quot;https://huchengv5.github.io/&quot;&gt;胡承&lt;/a&gt; 提供的错误和解决方法！&lt;/p&gt;

&lt;p&gt;你可能会在按照以上步骤操作后，在执行程序时遇到这样的错误，解决方法是“以管理员权限启动此程序”。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How to make Windows 8 desktop apps shown in Metro UI (like Task Manager)?&lt;br /&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/12873323/how-to-make-windows-8-desktop-apps-shown-in-metro-ui-like-task-manager&quot;&gt;https://stackoverflow.com/questions/12873323/how-to-make-windows-8-desktop-apps-shown-in-metro-ui-like-task-manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code Signing Notes&lt;br /&gt;
&lt;a href=&quot;http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Manifest+Manifest~Faqs.txt&quot;&gt;http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Manifest+Manifest~Faqs.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;UIAccess in Manifest Files&lt;br /&gt;
&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/4d2e1358-af95-4f4f-b239-68ec7e2525a9/uiaccess-in-manifest-files&quot;&gt;https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/4d2e1358-af95-4f4f-b239-68ec7e2525a9/uiaccess-in-manifest-files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Debug Applications with uiAccess Set to “True”&lt;br /&gt;
&lt;a href=&quot;http://blogs.techsmith.com/inside-techsmith/devcorner-debug-uiaccess/&quot;&gt;http://blogs.techsmith.com/inside-techsmith/devcorner-debug-uiaccess/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Debugging with uiAccess=true&lt;br /&gt;
&lt;a href=&quot;https://social.msdn.microsoft.com/forums/windowsdesktop/en-us/7a42efab-5ce8-456f-8a58-dfedbc2cefcb/debugging-with-uiaccesstrue&quot;&gt;https://social.msdn.microsoft.com/forums/windowsdesktop/en-us/7a42efab-5ce8-456f-8a58-dfedbc2cefcb/debugging-with-uiaccesstrue&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Apr 2020 03:50:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/run-desktop-application-above-windows-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/run-desktop-application-above-windows-application.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>C# 可空引用类型 Nullable 更强制的约束：将警告改为错误 WarningsAsErrors</title>
        <description>&lt;p&gt;程序员不看警告！&lt;/p&gt;

&lt;p&gt;于是 C# 8.0 带来的可空引用类型由于默认以警告的形式出现，所以实际上约束力非常弱。&lt;/p&gt;

&lt;p&gt;本文将把 C# 8.0 的可空引用类型警告提升为错误，以提高约束力。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;启用可空引用类型&quot;&gt;启用可空引用类型&lt;/h2&gt;

&lt;p&gt;你需要先在你的项目中启用可空引用类型的支持，才能修改警告到错误：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-enable-nullable-reference-types&quot;&gt;C# 8.0 如何在项目中开启可空引用类型的支持 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;项目属性&quot;&gt;项目属性&lt;/h2&gt;

&lt;p&gt;在项目属性中设置是比较快捷直观的方法。&lt;/p&gt;

&lt;p&gt;在项目上右键属性，打开“生成”标签。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-19-40-24.png&quot; alt=&quot;项目属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这里，可以看到“将警告视为错误”一栏：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;无&lt;/li&gt;
  &lt;li&gt;所有&lt;/li&gt;
  &lt;li&gt;特定警告&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以看到默认选中的是“特定警告”且值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NU1605&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;NU 是 NuGet 中发生的错误或者警告的前缀，&lt;code class=&quot;highlighter-rouge&quot;&gt;NU1605&lt;/code&gt; 是大家可能平时经常见到的一个编译错误“检测到包降级”。关于这个错误的信息可以阅读官网：&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1605&quot;&gt;NuGet Warning NU1605 - Microsoft Docs&lt;/a&gt;，本文不需要说明。&lt;/p&gt;

&lt;p&gt;于是，我们将我们需要视为错误的错误代码补充到后面就可以，以分号分隔。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NU1605;CS8600;CS8602;CS8603;CS8604;CS8618;CS8625
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这些值的含义可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/nullable-context-options-warnings&quot;&gt;C# 8.0 可空引用类型中的各项警告和错误 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;记得在改之前，把前面的配置从“活动”改为“所有配置”，这样你就不用改完之后仅在 Debug 生效，完了还要去 Release 配置再改一遍。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-19-46-46.png&quot; alt=&quot;改为所有配置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;warningsaserrors&quot;&gt;WarningsAsErrors&lt;/h2&gt;

&lt;p&gt;前面使用属性面板指定时，有一个奇怪的默认值。实际上我们直接修改将固化这个默认值，这不利于将来项目跟随 Sdk 或者 NuGet 包的升级。&lt;/p&gt;

&lt;p&gt;所以，最好我们能直接修改到项目文件，以便更精细地控制这个属性的值。&lt;/p&gt;

&lt;p&gt;在上一节界面中设置实际上是生成了一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;WarningsAsErrors&lt;/code&gt;。那么我们现在修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;WarningsAsErrors&lt;/code&gt; 属性的值，使其拼接之前的值：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;LangVersion&amp;gt;latest&amp;lt;/LangVersion&amp;gt;
        &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;WarningsAsErrors&amp;gt;$(WarningsAsErrors);CS8600;CS8601;CS8602;CS8603;CS8604;CS8609;CS8610;CS8614;CS8616;CS8618;CS8619;CS8622;CS8625&amp;lt;/WarningsAsErrors&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这句话的含义是先获取之前的值，将其放到我们要设置的值的前面。这样可以跟随 Sdk 或者 NuGet 包的升级而更新此默认值。&lt;/p&gt;

&lt;p&gt;这些值的含义可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/nullable-context-options-warnings&quot;&gt;C# 8.0 可空引用类型中的各项警告和错误 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tabsoverspaces.com/233764-switch-to-errors-instead-of-warnings-for-nullable-reference-types-in-csharp-8&quot;&gt;Switch to errors instead of warnings for nullable reference types in C# 8 - tabs ↹ over ␣ ␣ ␣ spaces by Jiří {x2} Činčura&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1605&quot;&gt;NuGet Warning NU1605 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 23 Apr 2020 12:17:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/warning-as-errors-for-csharp-nullable-reference-types.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/warning-as-errors-for-csharp-nullable-reference-types.html</guid>
        
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>C# 8.0 如何在项目中开启可空引用类型的支持</title>
        <description>&lt;p&gt;C# 8.0 引入了可为空引用类型和不可为空引用类型。由于这是语法级别的支持，所以比传统的契约式编程具有更强的约束力。更容易帮助我们消灭 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 异常。&lt;/p&gt;

&lt;p&gt;本文将介绍如何在项目中开启 C# 8.0 的可空引用类型的支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-sdk-风格的项目文件&quot;&gt;使用 Sdk 风格的项目文件&lt;/h2&gt;

&lt;p&gt;如果你还在使用旧的项目文件，请先升级成 Sdk 风格的项目文件：&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;本文会示例一个项目文件。&lt;/p&gt;

&lt;p&gt;由于现在 C# 8.0 还没有正式发布，所以如果要启用 C# 8.0 的语法支持，需要在项目文件中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;LangVersion&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;8.0&lt;/code&gt; 而不能指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;latest&lt;/code&gt; 等正式版本才能使用的值。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;8.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在项目文件中开启可空引用类型的支持&quot;&gt;在项目文件中开启可空引用类型的支持&lt;/h2&gt;

&lt;p&gt;在项目属性中添加一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableContextOptions&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;LangVersion&amp;gt;latest&amp;lt;/LangVersion&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此属性可被指定为以下四个值之一：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;enable&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;所有引用类型均被视为不可为空，启用所有 null 相关的警告。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;warnings&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;不会判定类型是否可空或不可为空，但启用局部范围内的 null 相关的警告。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;annotations&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;所有引用类型均被视为不可为空，但关闭 null 相关的警告。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;disable&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;与 8.0 之前的 C# 行为相同，即既不认为类型不可为空，也不启用 null 相关的警告。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这五个值其实是两个不同维度的设置排列组合之后的结果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;可为空注释上下文
    &lt;ul&gt;
      &lt;li&gt;用于告知编译器是否要识别一个类型的引用可为空或者不可为空。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;可为空警告上下文
    &lt;ul&gt;
      &lt;li&gt;用于告知编译器是否要启用 null 相关的警告，以及警告的级别。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当仅仅启用警告上下文而不开启可为空注释上下文，那么编译器将仅仅识别局部变量中明显可以判定出对 null 解引用的代码，而不会对包括变量或者参数定义部分进行分析。&lt;/p&gt;

&lt;h3 id=&quot;将警告视为错误&quot;&gt;将警告视为错误&lt;/h3&gt;

&lt;p&gt;以上只是警告，如果你希望更严格地执行可空引用的建议，可以考虑使用编译错误：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;LangVersion&amp;gt;latest&amp;lt;/LangVersion&amp;gt;
++      &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
++      &amp;lt;WarningsAsErrors&amp;gt;$(WarningsAsErrors);CS8600;CS8601;CS8602;CS8603;CS8604;CS8609;CS8610;CS8614;CS8616;CS8618;CS8619;CS8622;CS8625&amp;lt;/WarningsAsErrors&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/warning-as-errors-for-csharp-nullable-reference-types&quot;&gt;C# 可空引用类型 NullableReferenceTypes 更强制的约束：将警告改为错误 WarningsAsErrors - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/nullable-context-options-warnings&quot;&gt;C# 8.0 可空引用类型中的各项警告/错误的含义和示例代码 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;可为空注释annotation上下文&quot;&gt;可为空注释（Annotation）上下文&lt;/h3&gt;

&lt;p&gt;当启动可为空注释上下文后，C# 编译器会将所有的类型引用变量识别为以下种类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不可为空&lt;/li&gt;
  &lt;li&gt;可为空&lt;/li&gt;
  &lt;li&gt;未知&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，当你写出 &lt;code class=&quot;highlighter-rouge&quot;&gt;string walterlv&lt;/code&gt; 的变量定义，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 就是不可为空的引用类型；当写出 &lt;code class=&quot;highlighter-rouge&quot;&gt;string? walterlv&lt;/code&gt; 的变量定义，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 就是可为空的引用类型。&lt;/p&gt;

&lt;p&gt;对于类型参数来说，可能不能确定是否是可空引用类型，那么将视为“未知”。&lt;/p&gt;

&lt;p&gt;当关闭可为空注释上下文后，C# 编译器会将所有类型引用变量识别为以下种类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;无视&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，无论你使用什么方式顶一个一个引用类型的变量，C# 编译器都不会判定这到底是不是一个可为空还是不可为空的引用类型。&lt;/p&gt;

&lt;h3 id=&quot;可为空警告上下文&quot;&gt;可为空警告上下文&lt;/h3&gt;

&lt;p&gt;例如以下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在将 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 变量时，是不会引发程序异常的；而在后面调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 方法则会引发程序异常。&lt;/p&gt;

&lt;p&gt;安全性区别就在这里。安全性警告仅会将编译期间可识别到可能运行时异常的代码进行警告（即下面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.ToString()&lt;/code&gt;），而不会对没有异常的代码进行警告。如果是 &lt;code class=&quot;highlighter-rouge&quot;&gt;enable&lt;/code&gt;，那么将 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 变量的那一句也会警告。&lt;/p&gt;

&lt;h2 id=&quot;在源代码文件中开启可空引用类型的支持&quot;&gt;在源代码文件中开启可空引用类型的支持&lt;/h2&gt;

&lt;p&gt;除了在项目文件中全局开启可空引用类型的支持，也可以在 C# 源代码文件中覆盖全局的设定。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable enable&lt;/code&gt;: 在源代码中启用可空引用类型并给出警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable disable&lt;/code&gt;: 在源代码中禁用可空引用类型并关闭警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable restore&lt;/code&gt;: 还原这段代码中可空引用类型和可空警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable disable warnings&lt;/code&gt;: 在源代码中禁用可空警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable enable warnings&lt;/code&gt;: 在源代码中启用可空警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable restore warnings&lt;/code&gt;: 还原这段代码中可空警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable disable annotations&lt;/code&gt;: 在源代码中禁用可空引用类型。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable enable annotations&lt;/code&gt;: 在源代码中启用用可空引用类型。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#nullable restore annotations&lt;/code&gt;: 还原这段代码中可空引用类型。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;早期版本的属性&quot;&gt;早期版本的属性&lt;/h2&gt;

&lt;p&gt;在接近正式版的时候，开关才是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&lt;/code&gt;，而之前是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableContextOptions&lt;/code&gt;，但在 Visual Studio 2019 Preview 2 之前，则是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableReferenceTypes&lt;/code&gt;。现在，这些旧的属性已经废弃。&lt;/p&gt;

&lt;!-- 早期 `NullableContextOptions` 属性可被指定为以下五个值之一：

- `enable`
    - 所有引用类型均被视为不可为空，启用所有 null 相关的（Nullability）警告。
- `disable`
    - 无视所有引用类型是否为空，当设为此选项，则跟此前版本的 C# 行为一致。
- `safeonly`
    - 所有引用类型均被视为不可为空，启用所有安全性的 null 相关警告。
- `warnings`
    - 无视所有引用类型是否为空，但启用所有 null 相关的警告。
- `safeonlywarnings`
    - 无视所有引用类型是否为空，但启用所有安全性的 null 相关警告。

当前现在不用这么复杂了。

早期在项目中还可以通过 pragma 设置：

- `#pragma warning disable nullable`
- `#pragma warning enable nullable`
- `#pragma warning restore nullable`
- `#pragma warning safeonly nullable` --&gt;

&lt;h2 id=&quot;resharper-支持&quot;&gt;ReSharper 支持&lt;/h2&gt;

&lt;p&gt;ReSharper 从 2019.1.1 版本开始支持 C# 8.0，如果使用早期版本，就会到处报错。&lt;/p&gt;

&lt;p&gt;但是，由于 C# 8.0 可空引用类型的特性总在变，所以建议使用 2019.2.3 或以上版本，这是 C# 8.0 正式版本发布之后的 ReSharper。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references&quot;&gt;Nullable reference types - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/54855437/6233938&quot;&gt;c# - What is the difference between NullableContextOptions and NullableReferenceTypes? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 23 Apr 2020 12:17:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-enable-nullable-reference-types.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-enable-nullable-reference-types.html</guid>
        
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>C# 8.0 可空引用类型中的各项警告/错误的含义和示例代码</title>
        <description>&lt;p&gt;C# 8.0 引入了可为空引用类型和不可为空引用类型。当你需要给你或者团队更严格的要求时，可能需要定义这部分的警告和错误级别。&lt;/p&gt;

&lt;p&gt;本文将介绍 C# 可空引用类型部分的警告和错误提示，便于进行个人项目或者团队项目的配置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;开启可空引用类型以及配置警告和错误&quot;&gt;开启可空引用类型以及配置警告和错误&lt;/h2&gt;

&lt;p&gt;本文的内容本身没什么意义，但如果你试图进行一些团队配置，那么本文的示例可能能带来一些帮助。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-enable-nullable-reference-types&quot;&gt;C# 8.0 如何在项目中开启可空引用类型的支持 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/warning-as-errors-for-csharp-nullable-reference-types&quot;&gt;C# 可空引用类型 NullableReferenceTypes 更强制的约束：将警告改为错误 WarningsAsErrors - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;警告和错误&quot;&gt;警告和错误&lt;/h2&gt;

&lt;h3 id=&quot;cs8600&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8600&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;将 null 文本或可能的 null 值转换为非 null 类型。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-20-07-16.png&quot; alt=&quot;CS8600&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cs8601&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8601&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;可能的 null 引用赋值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 将可能为 null 的文本向不可为 null 的类型赋值。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8602&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8602&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;null 引用可能的取消引用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 当编译器判定 walterlv 可能为 null 时才会有此警告。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-20-08-52.png&quot; alt=&quot;CS8602&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cs8603&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8603&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;可能的 null 引用返回。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-20-12-35.png&quot; alt=&quot;CS8603&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cs8604&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8604&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;将可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的引用作为参数传递到不可为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的方法中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8609&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8609&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;返回类型中引用类型的为 Null 性与重写成员不匹配。&lt;/p&gt;

&lt;p&gt;比如你的基类中返回值不允许为 null，但是实现中返回值却允许为 null。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8610&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8610&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;参数中引用类型的为 Null 性与重写成员不匹配。&lt;/p&gt;

&lt;p&gt;比如你的基类中方法参数值不允许为 null，但是实现中方法参数却允许为 null。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8614&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8614&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;接口中定义的成员中的 null 性与实现中成员的 null 型不匹配。&lt;/p&gt;

&lt;p&gt;具体来说，你的接口中允许为 null，但是实现中却不允许为 null。&lt;/p&gt;

&lt;h3 id=&quot;cs8616&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8616&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;接口中定义的成员中的 null 性与实现中成员的 null 型不匹配。&lt;/p&gt;

&lt;p&gt;具体来说，你的接口中不允许为 null，但是实现中却允许为 null。&lt;/p&gt;

&lt;h3 id=&quot;cs8618&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8618&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;未初始化不可以为 null 的字段 “_walterlv”。&lt;/p&gt;

&lt;p&gt;如果一个类型中存在不可以为 null 的字段，那么需要在构造函数中初始化，如果没有初始化，则会发出警告或者异常。&lt;/p&gt;

&lt;h3 id=&quot;cs8619&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8619&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;一个类型与构造这个类型的 null 性不匹配。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8622&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8622&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;委托定义的参数中引用类型的为 null 性与目标委托不匹配。&lt;/p&gt;

&lt;p&gt;比如你定义了一个委托：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而在订阅事件的时候，使用的函数 null 性不匹配，则会出现警告：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 注意到这里的 object 本应该写作 object?&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cs8625&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8625&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;无法将 null 文本转换为非 null 引用或无约束类型参数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-20-10-39.png&quot; alt=&quot;CS8625&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cs8653&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CS8653&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;对于泛型 T，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt; 设置其值。如果 T 是引用类型，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt; 就会将这个泛型类型赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。然而并没有将泛型 T 的使用写为 T?。&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Apr 2020 12:16:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/nullable-context-options-warnings.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/nullable-context-options-warnings.html</guid>
        
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>git subtree 的使用</title>
        <description>&lt;p&gt;本文收集 git subtree 的使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;将-b-仓库添加为-a-仓库的一个子目录&quot;&gt;将 B 仓库添加为 A 仓库的一个子目录&lt;/h2&gt;

&lt;p&gt;在 A 仓库的根目录输入命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree add &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/B https://github.com/walterlv/walterlv.git master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，B 仓库的整体，会被作为 A 仓库中一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SubFolder/B&lt;/code&gt; 的子文件夹，同时保留 B 仓库中的整个日志记录。&lt;/p&gt;

&lt;h2 id=&quot;从-a-仓库中分离出一个子文件夹成为-b-仓库&quot;&gt;从 A 仓库中分离出一个子文件夹成为 B 仓库&lt;/h2&gt;

&lt;p&gt;在 A 仓库的根目录输入命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git subtree &lt;span class=&quot;nb&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/B &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; b-branch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，&lt;code class=&quot;highlighter-rouge&quot;&gt;SubFolder/B&lt;/code&gt; 文件夹便会被分离到新创建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;b-branch&lt;/code&gt; 分支。随后，如果你需要将分离的子文件夹推送到新仓库的话，直接将这个分支推送过去就可以了。&lt;/p&gt;

&lt;h2 id=&quot;将-a-仓库中的-b-子目录推送回-b-仓库&quot;&gt;将 A 仓库中的 B 子目录推送回 B 仓库&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree push &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/B https://github.com/walterlv/walterlv.git master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，如果你经常需要使用 subtree 命令，还是建议将那个远端设置一个别名，例如设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git remote add walterlv https://github.com/walterlv/walterlv.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，上面的命令可以简单一点：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree push &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/B walterlv master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后面，我们命令都会使用新的远端名称。&lt;/p&gt;

&lt;h2 id=&quot;将-b-仓库中的新内容拉回-a-仓库的子目录&quot;&gt;将 B 仓库中的新内容拉回 A 仓库的子目录&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree pull &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/B walterlv master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 17 Apr 2020 07:01:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-subtree-usage.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-subtree-usage.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>通过子类化窗口（SubClass）来为现有的某个窗口添加新的窗口处理程序（或者叫钩子，Hook）</title>
        <description>&lt;p&gt;创建窗口的时候，可以传一个消息处理函数。然而如果窗口不是自己创建的，还能增加消息处理函数吗？答案是可以的，除了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 来添加钩子之外，更推荐用子类化的方式来添加。&lt;/p&gt;

&lt;p&gt;本文介绍如何通过子类化（SubClass）的方式来为窗口添加额外的消息处理函数。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;子类化&quot;&gt;子类化&lt;/h2&gt;

&lt;p&gt;子类化的本质是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowLong&lt;/code&gt; 传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;GWL_WNDPROC&lt;/code&gt; 参数。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowLong&lt;/code&gt; 的 API 如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;LONG&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LONG&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;nIndex 指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;GWL_WNDPROC&lt;/code&gt;，在此情况下，后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dwNewLong&lt;/code&gt; 就可以指定为一个函数指针，返回值就是原始的消息处理函数。&lt;/p&gt;

&lt;p&gt;对于 .NET/C# 来说，我们需要拿到窗口句柄，拿到一个消息处理函数的指针。&lt;/p&gt;

&lt;p&gt;窗口句柄在不同的 UI 框架拿的方法不同，WPF 是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowInteropHelper&lt;/code&gt; 来拿。而将委托转换成函数指针则可通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.GetFunctionPointerForDelegate&lt;/code&gt; 来转换。&lt;/p&gt;

&lt;p&gt;你可别吐槽 WPF 另有它法来加消息处理函数啊！本文说的是 Win32，方法需要具有普适性。特别是那种你只能拿到一个窗口句柄，其他啥也不知道的窗口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnsureHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFunctionPointerForDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_WNDPROC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里处理消息。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将完整的代码贴下来，大约是这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MainWindow_SourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow_SourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnsureHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_wndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFunctionPointerForDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_wndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_WNDPROC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_wndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_NCHITTEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWindowProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWindowProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，我将委托存成了一个字段，这样可以避免 GC 回收掉这个委托对象造成崩溃。&lt;/p&gt;

&lt;p&gt;在示例的消息处理函数中，我示例处理了一下 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_NCHITTEST&lt;/code&gt;（虽然依然什么都没做）。最后，必须调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CallWindowProc&lt;/code&gt; 以调用此前原来的那个消息处理函数。&lt;/p&gt;

&lt;p&gt;最后，如果你又不希望处理这个消息了，那么使用以下方法注销掉这个委托：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 嗯，没错，就是前面更换消息处理函数时返回的那个指针。&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_WNDPROC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_originalWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面需要的所有的 P/Invoke 我都贴到了下面，需要的话放到你的代码当中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SetWindowLong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SetWindowLongPtr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLongPtr64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWindowProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpPrevWndFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_WNDPROC&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_NCHITTEST&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0084&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTRANSPARENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;其他方法&quot;&gt;其他方法&lt;/h2&gt;

&lt;p&gt;本文一开始说到了使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowsHookEx&lt;/code&gt; 的方式来添加钩子，具体你可以阅读我的另一篇博客来了解如何实现：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-global-windows-hook-in-dotnet.html&quot;&gt;.NET/C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/win32/winmsg/using-window-procedures?redirectedfrom=MSDN#subclassing_window&quot;&gt;Using Window Procedures - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 14 Apr 2020 00:19:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hook-a-window-by-sub-classing-it.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hook-a-window-by-sub-classing-it.html</guid>
        
        
        <category>win32</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>当无边框窗口被子窗口遮挡导致难以调节窗口大小时，可通过处理 NCHITTEST 消息重新支持调节窗口大小</title>
        <description>&lt;p&gt;做无边框窗口之后，我们有方法可以让窗口的标题栏区域和边缘调大小的区域继续正常工作，直到——这个窗口上面覆盖了其他的子窗口。这个子窗口会吃掉消息导致父窗口的边缘无法再继续处理这些消息。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;子窗口遮挡了父窗口&quot;&gt;子窗口遮挡了父窗口&lt;/h2&gt;

&lt;p&gt;看一下下面的动画，这个窗口的下半部分放了一个子窗口。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-11-two-windows.gif&quot; alt=&quot;被子窗口遮挡了边缘的父窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后尝试在边缘调节窗口尺寸，会发现被子窗口覆盖的部分是无法完成窗口大小调节的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-11-resize-with-child-windows.gif&quot; alt=&quot;子窗口区域无法调节窗口大小&quot; /&gt;&lt;/p&gt;

&lt;p&gt;究其原因，是子窗口处理掉了与调窗口大小相关的消息，导致父窗口完全不知道应该如何处理这个时候的操作。&lt;/p&gt;

&lt;h2 id=&quot;在子窗口处理消息循环&quot;&gt;在子窗口处理消息循环&lt;/h2&gt;

&lt;p&gt;在我的&lt;a href=&quot;/post/handle-nchittest-message-to-support-resize&quot;&gt;另一篇博客&lt;/a&gt;中，我有提到通过处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_NCHITTEST&lt;/code&gt; 消息，返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HT_RIGHT&lt;/code&gt; 等来实现支持 Windows 原生窗口功能的效果。然而那种方法是不适用于本文的场景的，如果你试试就会发现，那种方法会使得你只能调子窗口的大小，对父窗口无济于事。&lt;/p&gt;

&lt;p&gt;正确的处理方法是当鼠标划过原本应该处在非客户区部分的时候，将消息交给父窗口处理。于是，我们需要在消息循环的处理中返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTRANSPARENT&lt;/code&gt; 来告诉操作系统这个区域子窗口不处理消息，请交给父窗口。&lt;/p&gt;

&lt;p&gt;这里，我以 WPF 的消息循环来写代码。因为只要是 Windows 平台的 UI 框架都有消息循环的处理，所以可以很容易迁移到其他框架甚至是其他语言。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ChildWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChildWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChildWindow_SourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChildWindow_SourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_NCHITTEST&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0084&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTRANSPARENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_NCHITTEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 这里，我强行让所有区域返回 HTTRANSPARENT，于是整个子窗口都交给父窗口处理消息。&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 正常，你应该在这里计算窗口边缘。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTRANSPARENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的代码会比较简化，因为我让子窗口的所有区域都返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTRANSPARENT&lt;/code&gt;，这会让整个子窗口区域的消息都不由子窗口处理。如果需要使用这段代码的话，你需要自己判断窗口的边缘。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-11-resize-with-child-windows-2.gif&quot; alt=&quot;子窗口区域可以调节窗口大小&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果需要得到当前坐标的话，可以把下面的方法加入到你的项目中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lowOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;highOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetOrderWord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;low&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unchecked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;high&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unchecked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;low&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;high&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是将消息循环中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;lParam&lt;/code&gt; 传入可以获得当前的坐标（屏幕坐标系）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 获得当前基于屏幕坐标系的当前鼠标光标位置。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetOrderWord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;需要注意一些坑&quot;&gt;需要注意一些坑&lt;/h2&gt;

&lt;p&gt;当你准备使用返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTRANSPARENT&lt;/code&gt; 时，一定要保证你坐标所在的父子窗口在同一个线程！&lt;/p&gt;

&lt;p&gt;返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTRANSPARENT&lt;/code&gt; 时，操作系统只会查找同线程的其他窗口，如果你的父窗口非同一个线程，那么操作系统处理消息循环时是找不到下一个处理消息的窗口的。&lt;/p&gt;

&lt;p&gt;如果你一定要在父窗口非同一个线程时返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTRANSPARENT&lt;/code&gt; 那么你的整个窗口（顶层窗口和子窗口）将无法再操作！你可以阅读 &lt;a href=&quot;http://virtualdub.org/blog/pivot/entry.php?id=147&quot;&gt;HTTRANSPARENT is evil - virtualdub.org&lt;/a&gt; 了解相关的坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest?source=docs&quot;&gt;WM_NCHITTEST message (Winuser.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/33628963/wm-nchittest-and-httransparent-blocks-input-from-message-loop&quot;&gt;multithreading - WM_NCHITTEST and HTTRANSPARENT blocks input from message loop - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://php.developreference.com/article/17428986/WM_NCHITTEST+and+HTTRANSPARENT+blocks+input+from+message+loop&quot;&gt;WM_NCHITTEST and HTTRANSPARENT blocks input from message loop - multithreading&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/Windowsdesktop/en-US/a5e3cbbb-fd07-4343-9b60-6903cdfeca76/click-through-window-with-image-wpf-issues-httransparent-isnt-working?forum=csharplanguage&quot;&gt;Click through window with image (WPF) issues (HTTRANSPARENT isn’t working)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://virtualdub.org/blog/pivot/entry.php?id=147&quot;&gt;HTTRANSPARENT is evil - virtualdub.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/8969852/how-to-move-parent-window-without-border-from-child-using-wm-nchittest&quot;&gt;c++ - how to move parent window without border from child using WM_NCHITTEST - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/7913393/6233938&quot;&gt;winapi - Win api in C#. Get Hi and low word from IntPtr - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 Apr 2020 12:32:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/handle-nchittest-message-to-support-resize-even-if-window-is-covered-with-child-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/handle-nchittest-message-to-support-resize-even-if-window-is-covered-with-child-windows.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 系统的默认字体是什么？应用的默认字体是什么？</title>
        <description>&lt;p&gt;作为中文应用的开发者，我们多半会认为系统的默认字体是“微软雅黑”。然而如果真的产生了这种误解，则很容易在开发本地化应用的时候踩坑。&lt;/p&gt;

&lt;p&gt;于是本文带你了解 Windows 系统的默认字体。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-108187vista&quot;&gt;Windows 10/8.1/8/7/Vista&lt;/h2&gt;

&lt;p&gt;Windows 操作系统的默认字体是 Segoe UI（发音为 see go 这两个单词），默认的字体大小为 9 点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-18-21-29-13.png&quot; alt=&quot;Segoe UI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Segoe UI 是 Segoe 字体家族中专为显示器显示而设计的一款字体。当然，Windows 系统中的其他字体也遵循这一命名规则，带 UI 后缀的适用于界面显示，而不带 UI 后缀的适用于打印和其他排版设计。&lt;/p&gt;

&lt;p&gt;Segoe UI包含拉丁（Latin），希腊（Greek），西里尔字母（Cyrillic）和阿拉伯（Arabic）字符，覆盖了基本的英文俄文字母、数字和一些常用符号。然而其他语言就没有了。&lt;/p&gt;

&lt;p&gt;其他语言的默认字体分别是：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;语言&lt;/th&gt;
      &lt;th&gt;字体&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;日语（Japanese）&lt;/td&gt;
      &lt;td&gt;Yu Gothic UI&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;韩语（Korean）&lt;/td&gt;
      &lt;td&gt;Malgun Gothic&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;繁体中文（Chinese (Traditional)）&lt;/td&gt;
      &lt;td&gt;Microsoft JhengHei&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;简体中文（Chinese (Simplified)）&lt;/td&gt;
      &lt;td&gt;Microsoft YaHei&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;希伯来语（Hebrew）&lt;/td&gt;
      &lt;td&gt;Gisha&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;泰语（Thai）&lt;/td&gt;
      &lt;td&gt;Leelawadee&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;[注] 经 神樹桜乃 指出，日语系统默认字体是 Yu Gothic UI 而不是 Meiryo。&lt;/p&gt;

&lt;p&gt;Windows 操作系统在启动应用程序的时候，会根据当前系统用户的地区决定默认字体应该采用哪一个。&lt;/p&gt;

&lt;h2 id=&quot;windows-xp-及更早系统&quot;&gt;Windows XP 及更早系统&lt;/h2&gt;

&lt;p&gt;早期版本的 Windows，默认字体是 Tahoma。简体中文下则是宋体。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/uxguide/vis-fonts&quot;&gt;Fonts - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 Apr 2020 01:48:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-default-font-family.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-default-font-family.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何给 GitHub Pages 配置多个域名？</title>
        <description>&lt;p&gt;因为以前对域名进行了一些调整，所以实际上我的博客在历年来经历了两个域名 blog.walterlv.com（新）和 walterlv.com（旧）。然而 GitHub Pages 只支持一个自定义域名，所以为了兼容旧域名的访问，如何可以让多个域名对应同一个 GitHub Pages 呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;blog.walterlv.com&gt; （新）
&lt;/blog.walterlv.com&gt;
  &lt;/li&gt;
  &lt;li&gt;walterlv.com （旧）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以前不会碰到这样的问题，是因为我并没有使用 GitHub Pages 服务来构建博客，然而现在是了。&lt;/p&gt;

&lt;p&gt;GitHub Pages 识别访问的是哪个站点的方式是识别 xxx.github.io 的 xxx 部分，比如默认我只能通过 &lt;walterlv.github.io&gt; 来访问到我通过 GitHub Pages 搭建的博客。因此如果你使用反向代理服务器将一个其他的域名代理到 xxx.github.io 是会得到 404 的——GitHub Pages 不知道你想访问哪个站点。&lt;/walterlv.github.io&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-10-10-31-52.png&quot; alt=&quot;GitHub Pages 设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，在 GitHub Pages 设置里面，你就需要设置一个 Custom domain 来帮助 GitHub Pages 部署的时候知道某个域名实际上是你的，需要用来显示此仓库的 GitHub Pages。&lt;/p&gt;

&lt;p&gt;比如我在这里设置了 &lt;blog.walterlv.com&gt;，于是当我将反向代理服务器代理到 walterlv.github.io 时，GitHub Pages 便能正确得知这实际上是 walterlv.github.io 这个仓库的，这才能正确显示 GitHub Pages 页面。&lt;/blog.walterlv.com&gt;&lt;/p&gt;

&lt;p&gt;此设置会在你的仓库根目录生成 CNAME 文件，里面仅一行文本，即域名 &lt;blog.walterlv.com&gt;。&lt;/blog.walterlv.com&gt;&lt;/p&gt;

&lt;p&gt;然而问题来了，我之前的域名实际上是 walterlv.com，这样，当我设置 DNS 时，如果直接将 walterlv.com 设置到 walterlv.github.io 依然会出现 404。&lt;/p&gt;

&lt;p&gt;接下来我们说说解决办法。&lt;/p&gt;

&lt;h2 id=&quot;通过中转仓库&quot;&gt;通过中转仓库&lt;/h2&gt;

&lt;p&gt;我们需要在 GitHub 上再新建一个仓库，用来中转旧域名中的访问到新的域名。&lt;/p&gt;

&lt;h3 id=&quot;第一步新建随意名字的仓库&quot;&gt;第一步：新建随意名字的仓库&lt;/h3&gt;

&lt;p&gt;我们新建一个仓库。新建的时候实际上可以无所谓命名，因为这个仓库里面不会真的有内容，多数时候访问实际上是 404 的。但我们创建它只是为了前面提到的那个 CNAME 文件，告诉 GitHub Pages 我们有两个域名而已。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-10-14-32-47.png&quot; alt=&quot;新建仓库&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里，我创建了一个名为 oldblog 的仓库，正常情况下，访问这个仓库 GitHub Pages 的域名前缀为 walterlv.github.io/oldblog。&lt;/p&gt;

&lt;h3 id=&quot;第二步为此仓库添加-github-pages-服务&quot;&gt;第二步：为此仓库添加 GitHub Pages 服务&lt;/h3&gt;

&lt;p&gt;接着，按照平时去创建 GitHub Pages 服务的方法往这个仓库提交代码。&lt;/p&gt;

&lt;p&gt;例如可以在仓库根目录放一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; 文件（这是 Jekyll 的配置文件），然后直接提交：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;walterlv&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;walterlv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当有了一个分支和 Jekyll 的配置文件后，就可以直接使用 GitHub Pages 服务了。在这里，我们将自定义域名填写成旧的域名 walterlv.com。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-10-14-42-36.png&quot; alt=&quot;为新建的仓库配置 GitHub Pages&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样，当我们在域名服务器中将 walterlv.com 设置到 walterlv.github.io 时，GitHub Pages 至少知道应该使用这个仓库里的 GitHub Pages 来显示。&lt;/p&gt;

&lt;h3 id=&quot;第三步创建用于跳转的-404-页面&quot;&gt;第三步：创建用于跳转的 404 页面&lt;/h3&gt;

&lt;p&gt;然而我们并不打算在这个仓库里真的放代码/网页，于是在根目录放一个 404.html 文件：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;正在重定向…… - walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;blog.walterlv.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        正在重定向……
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，仓库里面是这样的（&lt;a href=&quot;https://github.com/walterlv/oldblog&quot;&gt;walterlv/oldblog&lt;/a&gt;）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-10-14-54-31.png&quot; alt=&quot;仓库文件&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第四步配置-dns&quot;&gt;第四步：配置 DNS&lt;/h3&gt;

&lt;p&gt;最后检查你的 DNS 配置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;旧域名：walterlv.com -&amp;gt; walterlv.github.io&lt;/li&gt;
  &lt;li&gt;新域名：&lt;blog.walterlv.com&gt; -&amp;gt; walterlv.github.io&lt;/blog.walterlv.com&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;到现在，就全部完成。不信你试试，点击链接 &lt;a href=&quot;https://walterlv.com/post/multiple-domains-for-github-pages&quot;&gt;https://walterlv.com/post/multiple-domains-for-github-pages&lt;/a&gt; 会短暂进入一个“正在重定向……”的页面，然后随即跳转到新域名下相同的页面 &lt;a href=&quot;https://blog.walterlv.com/post/multiple-domains-for-github-pages&quot;&gt;https://blog.walterlv.com/post/multiple-domains-for-github-pages&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;原理&quot;&gt;原理&lt;/h3&gt;

&lt;p&gt;一个 GitHub Pages 的仓库只能有一个 CNAME 文件，也即我们只能告知 GitHub 我们的一个合理域名。要让 GitHub Pages 支持两个域名，我们不得不建两个仓库，其中第二个仓库的地址为 xxx.walterlv.com/repo-name。在第二个仓库中，我们故意什么都不放，这样会触发 404，我们在 404 页面里面跳转到新的域名即完成了我们的目的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.yanbinghu.com/2019/03/29/25951.html&quot;&gt;多个域名映射同一个github pages - 守望的个人博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 10 Apr 2020 07:02:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/multiple-domains-for-github-pages.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/multiple-domains-for-github-pages.html</guid>
        
        
        <category>github</category>
        
      </item>
    
      <item>
        <title>一点点从坑里爬出来：如何正确打开 WPF 里的 Popup？</title>
        <description>&lt;p&gt;在 WPF 中打开一个 Popup 并没有想象当中容易。虽说提供了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsOpen&lt;/code&gt; 属性用于显示 Popup，但实际上造成的 Bug 会让你解得死去活来。Win32 的 WS_POPUP 也坑，不过 WPF 会额外再带来一些，所以本文只说 WPF。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;先说结论&quot;&gt;先说结论&lt;/h2&gt;

&lt;p&gt;本文一开始就贴出打开一个 Popup 的代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 在以下代码中，我们假定 popup 是我们要显示出来的 Popup，而 textBox 是 Popup 中的文本框。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemoControl_MouseUp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MouseButtonEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 必须延迟打开 Popup，如果在 MouseUp 中打开，会使得 Popup 无法获得焦点。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;popup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsOpen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 必须显式让 Popup 获得焦点，否则内部的 TextBox 输入时，IME 输入框无法跟随。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;popup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SetFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 必须显式让文本框获得焦点（如果有的话）。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你的 Popup 中没有文本框，那么最后的两段可以删除。&lt;/p&gt;

&lt;p&gt;接下来一一说明。&lt;/p&gt;

&lt;h2 id=&quot;不要在-mouseupclick-事件中打开-popup&quot;&gt;不要在 MouseUp/Click 事件中打开 Popup&lt;/h2&gt;

&lt;p&gt;Popup 有一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;StaysOpen&lt;/code&gt;，当设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 时，我们期待的效果是失焦后 Popup 关闭。然而如果你是在任何控件的 MouseUp 事件中打开的，那么 Popup 就不会获得焦点。既然不会获得焦点，那么也就不存在失焦的问题。&lt;/p&gt;

&lt;p&gt;具体表现为，你打开了 Popup 后，Popup 不会自己再自动关闭了，除非你手动在 Popup 内部点一下让 Popup 获得焦点，随后才会自动关闭。&lt;/p&gt;

&lt;p&gt;无论你在后面如何写让 Popup 以及内部控件获得焦点的代码，实际上这种情况下弹出的 Popup 不会真正获得焦点，除非手动点击。&lt;/p&gt;

&lt;p&gt;所以我在以上代码中加上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;await Task.Yield()&lt;/code&gt; 这样可以让后续的代码不再在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseUp&lt;/code&gt; 事件中。&lt;/p&gt;

&lt;p&gt;如果你的 Popup 中没有文本框，那么这样做就够了；如果有，那么还需要做后续处理。&lt;/p&gt;

&lt;h2 id=&quot;需要显式为-popup-设置焦点&quot;&gt;需要显式为 Popup 设置焦点&lt;/h2&gt;

&lt;p&gt;注意注意，如果你的 Popup 中包含文本框，那么一定需要加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetFocus&lt;/code&gt; 的调用。WPF 版本的设置焦点，无论是逻辑焦点（&lt;code class=&quot;highlighter-rouge&quot;&gt;xx.Focus()&lt;/code&gt;）还是键盘焦点（&lt;code class=&quot;highlighter-rouge&quot;&gt;Keyboard.Focus(xx)&lt;/code&gt;）都无法真正让 Popup 获得焦点。这时打字，IME 框是不会跟随文本框的。&lt;/p&gt;

&lt;h2 id=&quot;需要单独为-textbox-再设置焦点&quot;&gt;需要单独为 TextBox 再设置焦点&lt;/h2&gt;

&lt;p&gt;只是为 Popup 设置焦点的话，Popup 中的文本框没有获得焦点，是不能直接打字的。当然你可能需求如此。这里就没有特别说明的点了。&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2020 13:34:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-open-a-wpf-popup.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-open-a-wpf-popup.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>VSCode：当匹配到结果时，如何一次性全部选中操作（复制/删除）？</title>
        <description>&lt;p&gt;最近需要处理几十万行的文字，然后提取出数千行（嗯，我在做输入法词库）。在 VSCode 里我用正则匹配到了想要的结果后，如何能够快速把这些行提取出来呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;其实非常简单，Alt + Enter 即可选中所有已经匹配到的文字。&lt;/p&gt;

&lt;p&gt;来，我们看这个具体的例子：&lt;/p&gt;

&lt;p&gt;这里有一个几十万行的词库，我需要将其中的英文部分提取出来做成单独的词库。于是我使用正则表达式，匹配到所有英文词。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-07-11-04-56.png&quot; alt=&quot;匹配文字&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接着，按下 Alt + Enter 我就可以复制出所有的已匹配的词。将其粘贴出来即形成新的纯英文词库。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-07-11-08-48.png&quot; alt=&quot;已选中文字&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-07-11-09-39.png&quot; alt=&quot;新的词库文件&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2020 12:48:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-select-all-matched-search-result-in-vscode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-select-all-matched-search-result-in-vscode.html</guid>
        
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>最简单的代码，让 WPF 支持响应式布局</title>
        <description>&lt;p&gt;响应式布局在各种现代的 UI 框架中不是什么新鲜的概念，基本都是内置支持。然而在古老的 WPF 框架中却并没有原生支持，后来虽然通过 Blend 自带的 Interactions 库实现了响应式布局，但生成的代码量太大了，而且需要引入额外的库。&lt;/p&gt;

&lt;p&gt;如果只是希望临时局部地方使用响应式布局，那么其实可以直接使用 WPF 内置的绑定机制来完成响应式布局。本文介绍如何使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;思路是在控件尺寸发生变更的时候更新控件的样式。而能容易实现这个的只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Trigger&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Setter&lt;/code&gt; 那一套。直接在控件上使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Trigger&lt;/code&gt; 只能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EventTrigger&lt;/code&gt;，因此我们需要编写能写更多种类 &lt;code class=&quot;highlighter-rouge&quot;&gt;Trigger&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Style.Foo.WalterlvDemo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid.Row&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid.Column&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style.Triggers&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DataTrigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
                     &lt;span class=&quot;na&quot;&gt;Binding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding ActualHeight, ElementName=DemoWindow,
                              Converter={StaticResource GreaterOrEqualsConverter},
                              ConverterParameter=640}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid.Row&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid.Column&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DataTrigger&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style.Triggers&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;定义了一个样式，默认情况下，行列是 (0, 0)，当窗口宽度大于或等于 640 之后，行列换到 (1, 1)。&lt;/p&gt;

&lt;p&gt;这里我们需要一个大于或等于，以及小于的转换器。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Globalization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Cvte.EasiNote.UI.Styles.Converters&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GreaterOrEqualsConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IValueConverter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ulong&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertBack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LessConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IValueConverter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ulong&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertBack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你本身是写的基础控件的样式，那么绑定当然就跟本文一开始说的写法非常类似了。&lt;/p&gt;

&lt;p&gt;如果你需要写的是一般控件，可以考虑直接在控件里写 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Framework.Style /&amp;gt;&lt;/code&gt; 把样式内联进去。&lt;/p&gt;

&lt;p&gt;如果你写的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt;，也一样是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTrigger&lt;/code&gt; 绑定。&lt;/p&gt;

&lt;p&gt;你也可以不绑定到窗口上，而绑定到控件本身上，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TemplatedParent&lt;/code&gt; 作为绑定的源即可。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DataTemplate.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;local:LessConverter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LessThan60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Than=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DataTemplate.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Icon&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Mask&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Red&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DataTemplate.Triggers&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DataTrigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Binding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource LessThan60}}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grid.ColumnSpan&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Mask&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visibility&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Collapsed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DataTrigger&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 04 Apr 2020 14:55:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-adaptive-ui-in-simplest-way.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-adaptive-ui-in-simplest-way.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF：无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内，在另一范围内定义它时，已注册了名称。</title>
        <description>&lt;p&gt;最近在改一段 XAML 代码时，我发现无论如何给一个控件添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Name&lt;/code&gt; 属性时都会出现编译错误：无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内，在另一范围内定义它时，已注册了名称。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编译错误&quot;&gt;编译错误&lt;/h2&gt;

&lt;p&gt;编译时，出现错误：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内，在另一范围内定义它时，已注册了名称。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;MC3093: Cannot set Name attribute value ‘X’ on element ‘Y’. ‘Y’ is under the scope of element ‘Z’, which already had a name registered when it was defined in another scope.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里的 XXX 是元素的类型，YYY 是指定的名称的值，ZZZ 是父容器的名称。&lt;/p&gt;

&lt;p&gt;我把出现错误的 XAML 简化后大约是这样的，&lt;code class=&quot;highlighter-rouge&quot;&gt;XXX&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextBox&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;YYY&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenameTextBox&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ZZZ&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv:Foo&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;walterlv:Foo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Orientation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Focusable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;名称：&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RenameTextBox&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/walterlv:Foo&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;小心用户控件&quot;&gt;小心用户控件&lt;/h2&gt;

&lt;p&gt;出现此问题的最大原因在那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv:Foo&lt;/code&gt; 上。实际上，这是一个用户控件，也就是继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;UserControl&lt;/code&gt; 的大家通常用来写界面的东西。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Foo&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl.Style&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl.Style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;别问我为什么会有以上这样诡异的代码。我也不知道，这只是偶然发现的代码，我简化后拿到博客中。&lt;/p&gt;

&lt;p&gt;于是需要提醒大家注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 WPF 里，拥有直接的 XAML 文件的始终应该作为最终用户界面，不应该当作控件使用（不要试图在其他地方使用时还设置其 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content&lt;/code&gt; 属性）；&lt;/li&gt;
  &lt;li&gt;如果你确实希望做控件，请继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomControl&lt;/code&gt; 然后在 &lt;code class=&quot;highlighter-rouge&quot;&gt;/Themes/Generic.xaml&lt;/code&gt; 里写样式。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至于以上 XAML 代码中我看到用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;UserControl.Style&amp;gt;&lt;/code&gt; 来写样式，是因为踩到了当控件用的另一个坑：&lt;/p&gt;

&lt;p&gt;所有在控件的 XAML 中设置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content&lt;/code&gt; 属性都将被使用时覆盖。&lt;/p&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;p&gt;当然是考虑将以上诡异的用户控件定义方式改为正统的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomControl&lt;/code&gt; 啦！将 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;UserControl.Style&amp;gt;&lt;/code&gt; 里定义的所有样式全部改到 &lt;code class=&quot;highlighter-rouge&quot;&gt;/Themes/Generic.xaml&lt;/code&gt; 文件中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-03-14-40-15.png&quot; alt=&quot;创建自定义控件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你不清楚如何编写一个自定义控件，那么请直接在 Visual Studio 中基于 WPF 自定义控件创建文件，你会发现 Visual Studio 为你写好了注释。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media.Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Navigation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Shapes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 按照步骤 1a 或 1b 操作，然后执行步骤 2 以在 XAML 文件中使用此自定义控件。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 元素中:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     xmlns:MyNamespace=&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 元素中:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     xmlns:MyNamespace=&quot;clr-namespace:Walterlv.Demo;assembly=Walterlv.Demo&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用，&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 并重新生成以避免编译错误:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     在解决方案资源管理器中右击目标项目，然后依次单击&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     “添加引用”-&amp;gt;“项目”-&amp;gt;[浏览查找并选择此项目]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 步骤 2)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 继续操作并在 XAML 文件中使用控件。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;MyNamespace:Foo/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Control&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DefaultStyleKeyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OverrideMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FrameworkPropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/Themes/Generic.xaml&lt;/code&gt; 文件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;


    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Type local:Foo}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Template&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Type local:Foo}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;
                            &lt;span class=&quot;na&quot;&gt;BorderBrush=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding BorderBrush}&quot;&lt;/span&gt;
                            &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding BorderThickness}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 03 Apr 2020 06:44:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/cannot-set-name-attribute-value-on-element-using-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/cannot-set-name-attribute-value-on-element-using-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 2019 (16.5) 中查看托管线程正在等待的锁被哪个线程占用</title>
        <description>&lt;p&gt;Visual Studio 2019 (16.5) 版本更新中带来了一项很小很难注意到却非常实用的功能，查看哪一个托管线程正在持有 .NET 对象锁。&lt;/p&gt;

&lt;p&gt;如果你不了解这个功能如何使用，那么可以阅读本文。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;更新日志&quot;&gt;更新日志&lt;/h2&gt;

&lt;p&gt;Visual Studio 的官方更新日志中对此功能的描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;View which managed thread is holding a .NET object lock&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即“查看托管线程正在持有 .NET 对象锁”。&lt;/p&gt;

&lt;h2 id=&quot;功能入口&quot;&gt;功能入口&lt;/h2&gt;

&lt;p&gt;这个功能没有新的入口，你可以在“调用堆栈” (Call Stack) 窗口，“并行堆栈” (Parallel Stacks) 窗口，以及“线程”窗口的位置列中查看哪个托管线程正在持有 .NET 对象锁。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/visualstudio/wp-content/uploads/sites/4/2020/03/165GADebugger2.png&quot;&gt;Call Stack&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;示例&quot;&gt;示例&lt;/h2&gt;

&lt;p&gt;现在我们就实际看一下这个功能的用法和效果。于是我写了一点下面的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;后台线程尝试获得锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;后台线程成功获得锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv thread&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;主线程尝试获得锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Monitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;主线程成功获得锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，主线程获得锁之后直接退出，而新线程“walterlv thread”则尝试获得锁。&lt;/p&gt;

&lt;p&gt;现在在 Visual Studio 2019 中运行这段代码，可以看到另一个线程是不可能获得锁的，于是不会输出最后那一句，其他都会输出。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-02-17-36-40.png&quot; alt=&quot;锁&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后我们在 Visual Studio 中点击“全部中断”，也就是那个“暂停”图标的按钮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-02-17-37-25.png&quot; alt=&quot;全部中断&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开调用堆栈窗口（在“调试 -&amp;gt; 窗口 -&amp;gt; 调用堆栈”），可以看到堆栈最顶端显示了正在等待锁，并且指出了线程对象。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-02-17-39-24.png&quot; alt=&quot;正在等待某个线程的锁&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在线程窗口（在“调试 -&amp;gt; 窗口 -&amp;gt; 线程“）的位置列，鼠标移上去可以看到与堆栈中相同的信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-02-17-40-44.png&quot; alt=&quot;在线程窗口中查看&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，我们的主线程实际上早已直接退出了，所以正在等待的锁将永远不会释放（除非进程退出）。&lt;/p&gt;

&lt;p&gt;同样的信息，在并行堆栈（在“调试 -&amp;gt; 窗口 -&amp;gt; 并行堆栈”）中也能看到。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-04-02-17-43-47.png&quot; alt=&quot;在并行堆栈中查看&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes&quot;&gt;Visual Studio 2019 version 16.5 Release Notes - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/visualstudio/visual-studio-2019-version-16-5/&quot;&gt;Visual Studio 2019 version 16.5 is now available - Visual Studio Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 02 Apr 2020 09:44:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/view-which-managed-thread-is-holding-a-dotnet-object-lock-using-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/view-which-managed-thread-is-holding-a-dotnet-object-lock-using-visual-studio.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>WPF 中如何绑定附加属性？XAML 中记得加括号，C# 中记得不能用字符串</title>
        <description>&lt;p&gt;在 XAML 中写绑定是 WPF 学习的必修课，进阶一点的，是用 C# 代码来写绑定。然而一旦绑定的属性是附加属性，好多小伙伴就会开始遇到坑了。&lt;/p&gt;

&lt;p&gt;本文将介绍如何在 XAML 和 C# 代码中绑定附加属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景代码&quot;&gt;背景代码&lt;/h2&gt;

&lt;p&gt;开始遇到这个问题的背景是我定义了一个附加属性，然后试图通过绑定的方式完成一些业务。&lt;/p&gt;

&lt;p&gt;用附加属性来完成的很大一个好处在于不需要改动原有的代码破坏原来的类。例如我只需要在任何一个类中定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsDraggable&lt;/code&gt; 附加属性，就可以让我其他地方的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt; 等支持拖拽。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DraggableElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TabViewItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DefaultStyleKeyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OverrideMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DraggableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FrameworkPropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DraggableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsDraggableProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;IsDraggable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabViewItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetIsDraggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsDraggableProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetIsDraggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsDraggableProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在-xaml-中绑定附加属性&quot;&gt;在 XAML 中绑定附加属性&lt;/h2&gt;

&lt;p&gt;在 XAML 中绑定附加属性的时候需要加上括号和类型的命名空间前缀：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ListViewItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding (local:DraggableElement.IsDraggable), RelativeSource={RelativeSource Self}}&quot;&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;local:DraggableElement.IsDraggable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于 WPF 内置的命名空间（&lt;code class=&quot;highlighter-rouge&quot;&gt;http://schemas.microsoft.com/winfx/2006/xaml/presentation&lt;/code&gt; 命名空间下），是不需要加前缀的。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DemoTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding (Grid.Row), RelativeSource={RelativeSource Self}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;跟其他的绑定一样，这里并不需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 后面写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path=&lt;/code&gt;，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 的构造函数中传入的参数就是赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 的。&lt;/p&gt;

&lt;h2 id=&quot;在-c-代码中绑定附加属性&quot;&gt;在 C# 代码中绑定附加属性&lt;/h2&gt;

&lt;p&gt;上面在说明附加属性绑定的时候我特地额外写了一个不需要写命名空间的 XAML 绑定附加属性的代码，这是为了说明接下来写 C# 代码时的注意事项。&lt;/p&gt;

&lt;p&gt;是这样写吗？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 给不看全文的小伙伴：这段代码是无法工作的！正常工作的在后文。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(Grid.Row)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;设想应该不是，因为 C# 代码中是没有命名空间前缀的，于是对于前面 XAML 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;(local:DraggableElement.IsDraggable)&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;local&lt;/code&gt; 部分就很不好处理。&lt;/p&gt;

&lt;p&gt;实际上，这里的字符串即便是写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Grid.Row&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.BindingDemo.DraggableElement.IsDraggable&lt;/code&gt; 也依然会绑定失败。&lt;/p&gt;

&lt;p&gt;在 C# 代码中绑定附加属性，需要 &lt;strong&gt;使用依赖项属性，而不能使用字符串&lt;/strong&gt;！&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RowProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoDraggableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DraggableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsDraggableProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DemoDraggableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因此需要特别注意，附加属性的绑定不再能使用字符串，需要使用依赖项属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://geekswithblogs.net/NewThingsILearned/archive/2008/01/15/binding-to-an-attached-property.aspx&quot;&gt;Binding to an Attached Property&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 01 Apr 2020 12:35:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-bind-attached-properties-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-bind-attached-properties-in-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>将 Windows Terminal 作为外部工具集成到其他工具/程序/代码中</title>
        <description>&lt;p&gt;Windows Terminal 在 Windows 上是一款 UWP 应用，然而其依然具有良好的与外部工具的集成特性，你可以在其他各种工具中配置使用 Windows Terminal 打开。&lt;/p&gt;

&lt;p&gt;本文介绍如何配置使用 Windows Terminal 打开。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;命令行调用&quot;&gt;命令行调用&lt;/h2&gt;

&lt;p&gt;在应用商店可以下载到 &lt;a href=&quot;https://www.microsoft.com/store/productId/9N0DX20HK701&quot;&gt;Windows Terminal (Preview)&lt;/a&gt;，下载安装后，你就可以开始使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;wt&lt;/code&gt; 命令了，这可以用来启动 Windows Terminal。（这里要说明一下，虽然你可以找到应用程序在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_0.9.433.0_x64__8wekyb3d8bbwe\WindowsTerminal.exe&lt;/code&gt; 下，但是你并没有权限直接去运行 UWP 应用的 exe 入口。&lt;/p&gt;

&lt;p&gt;因此，你在任意的命令行工具，甚至是 Win+R 运行窗口，或者开始菜单的搜索中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;wt&lt;/code&gt; 回车就可以运行 Windows Terminal 了。&lt;/p&gt;

&lt;p&gt;增加的命令实际上来自于 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\AppData\Local\Microsoft\WindowsApps&lt;/code&gt; 目录。你可以进入这个目录找到商店应用增加的所有的命令。&lt;/p&gt;

&lt;p&gt;默认情况下直接打开会进入用户文件夹下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-17-09-52-28.png&quot; alt=&quot;默认打开&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果需要在特定的工作目录下打开，则需要修改配置。请点击设置按钮打开配置文件，然后修改默认终端的 &lt;code class=&quot;highlighter-rouge&quot;&gt;startingDirectory&lt;/code&gt; 属性，从 &lt;code class=&quot;highlighter-rouge&quot;&gt;%USERPROFILE%&lt;/code&gt; 修改到其他路径：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-17-09-53-59.png&quot; alt=&quot;打开设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果需要使用“当前工作路径”，则将 &lt;code class=&quot;highlighter-rouge&quot;&gt;startingDirectory&lt;/code&gt; 修改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;%__CD__%&lt;/code&gt;。注意，CD 两边分别是两个下划线。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    {
      ……
      &quot;snapOnInput&quot;: true,
&lt;span class=&quot;gd&quot;&gt;--    &quot;startingDirectory&quot;: &quot;%USERPROFILE%&quot;,
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++    &quot;startingDirectory&quot;: &quot;%__CD__%&quot;,
&lt;/span&gt;      &quot;useAcrylic&quot;: true
    },
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在修改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;%__CD__%&lt;/code&gt; 之后，如果通过快捷方式直接启动 Windows Terminal，则会看到路径被切换到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\System32&lt;/code&gt;。不过这不重要，因为即便是选择了用户路径，每次启动也都是要切走的。&lt;/p&gt;

&lt;h2 id=&quot;工具集成&quot;&gt;工具集成&lt;/h2&gt;

&lt;p&gt;在了解了以上命令行调用后，工具集成就简单多了，只需要设置好启动 &lt;code class=&quot;highlighter-rouge&quot;&gt;wt&lt;/code&gt; 命令，以及设置好工作路径即可。&lt;/p&gt;

&lt;p&gt;如下图是我在 Directory Opus 中设置的 Windows Terminal 的一键打开按钮：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-17-10-02-46.png&quot; alt=&quot;在 Directory Opus 中设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于 Directory Opus 集成工具可以参见我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/directory-opus-custom-toolbar-buttons.html&quot;&gt;在 Directory Opus 中添加自定义的工具栏按钮提升效率 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/directory-opus-integrate-with-terminals.html&quot;&gt;Directory Opus 使用命令编辑器添加 PowerShell / CMD / Bash 等多种终端到自定义菜单 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-代码调用&quot;&gt;C# 代码调用&lt;/h2&gt;

&lt;p&gt;使用 C# 代码启动的方法也非常常规，直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 然后设置工作路径即可。前提是前面设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;%__CD__%&lt;/code&gt; 为启动路径。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;wt.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;UseShellExecute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblog.west-wind.com/posts/2019/Sep/03/Programmatically-Opening-Windows-Terminal-in-a-Specific-Folder&quot;&gt;Programmatically Opening Windows Terminal in a Specific Folder - Rick Strahl’s Web Log&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 24 Mar 2020 01:49:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-windows-terminal-external-starting-support-with-working-directory.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-windows-terminal-external-starting-support-with-working-directory.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>设置用户无需密码自动登录到 Windows 系统</title>
        <description>&lt;p&gt;你一定要为你的 Windows 用户账户设置密码，一来会安全一些，而来可以远程登录使用；但有时出于一些特殊的目的，不希望在每次开机后都必须输入密码才能进入系统。于是你可以使用本文提供的方法在每次开机的时候免密码登录到 Windows 操作系统。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;步骤&quot;&gt;步骤&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 Windows 搜索框中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;netplwiz&lt;/code&gt;，然后回车打开命令；&lt;/li&gt;
  &lt;li&gt;去掉“要使用本计算机，用户必须输入用户名和密码”的勾勾；&lt;/li&gt;
  &lt;li&gt;点击“确定”或“应用”后，输入自动登录账号的用户名和密码。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，输入用户名和密码的时候，如果你使用了微软账号登录，那么需要输入你的微软账号，比如这样“walterlv@outlook.com”；而密码是你微软账号的密码，而不是 PIN 码。&lt;/p&gt;

&lt;h2 id=&quot;windows-10-截图&quot;&gt;Windows 10 截图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-08-58-32.png&quot; alt=&quot;netplwiz&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-08-57-14.png&quot; alt=&quot;高级用户账户控制面板&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;windows-7-截图&quot;&gt;Windows 7 截图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-08-54-26.png&quot; alt=&quot;netplwiz&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-08-55-58.png&quot; alt=&quot;高级用户账户控制面板&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 23 Mar 2020 03:36:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/automatically-sign-in-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/automatically-sign-in-windows.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>修复 Windows 10 设置界面里面混乱的语言翻译</title>
        <description>&lt;p&gt;Windows 10 每次新发布一个版本都会遇到各种各样的新型 Bug。&lt;/p&gt;

&lt;p&gt;本文介绍的是 Windows 10 的设置界面里面，各种各样的语言文字都很混乱，就像统一错位了一样。本文也会同时介绍其修复方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系统版本&quot;&gt;系统版本&lt;/h2&gt;

&lt;p&gt;会出现此问题的系统是 Windows 10 英文版系统。&lt;/p&gt;

&lt;p&gt;注意，是 Windows 10 英文版系统，而不是中文版系统的英文语言。如果你想要识别这样的系统的话，也很简单，使用你的系统安装程序，安装程序中界面使用的语言就是此系统的原生语言。&lt;/p&gt;

&lt;p&gt;如果在安装完此英文版系统后再安装中文语言，就可能会出现中文语言混乱的问题。&lt;/p&gt;

&lt;h2 id=&quot;混乱的界面&quot;&gt;混乱的界面&lt;/h2&gt;

&lt;p&gt;先看看下面的两张图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-09-56-38.png&quot; alt=&quot;混乱的界面1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-09-57-25.png&quot; alt=&quot;混乱的界面2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以注意到，界面当中出现了很多本不应该出现在那个地方的文案。&lt;/p&gt;

&lt;p&gt;如果我们这个时候让设置界面弹出一个对话框出来，你还会看到对话框中的文字超出范围导致布局错乱呢：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-09-59-24.png&quot; alt=&quot;混乱的界面3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;按钮都不知道被裁成什么样了。&lt;/p&gt;

&lt;h2 id=&quot;修复方法&quot;&gt;修复方法&lt;/h2&gt;

&lt;p&gt;经过我的多次尝试，发现，英文版系统安装中文语言包，第一次几乎必定失败，然后出现本文所述的问题。&lt;/p&gt;

&lt;p&gt;解决方法是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将语言切换回英文&lt;/li&gt;
  &lt;li&gt;删除中文语言包&lt;/li&gt;
  &lt;li&gt;删除下载缓存文件&lt;/li&gt;
  &lt;li&gt;重新下载中文语言包&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面详细说明。&lt;/p&gt;

&lt;h3 id=&quot;将语言切换回英文&quot;&gt;将语言切换回英文&lt;/h3&gt;

&lt;p&gt;进入“系统设置 -&amp;gt; 时间和语言 -&amp;gt; 语言”，通过点击上箭头的方式将英语语言置顶，同时将显示语言切换成英语。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-11-20-43.png&quot; alt=&quot;置顶英语语言&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-13-25-56.png&quot; alt=&quot;将显示语言切换成英语&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;删除中文语言包&quot;&gt;删除中文语言包&lt;/h3&gt;

&lt;p&gt;以管理员权限启动 PowerShell，然后输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-WinUserLanguageList&lt;/code&gt; 命令，以获取我们要删除的语言的 LanguageTag。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-WinUserLanguageList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LanguageTag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;en-US&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Autonym&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;United&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnglishName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LocalizedName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;United&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScriptName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Latin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InputMethodTips&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0409&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00000409&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Spellchecking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handwriting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LanguageTag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;zh-Hans-CN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Autonym&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中文&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中华人民共和国&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnglishName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Chinese&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LocalizedName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Chinese&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Simplified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;China&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScriptName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Chinese&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Simplified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InputMethodTips&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0804&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;81&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D4E9C9-1D3B-41BC-9E6C-4B40BF79E35E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FA550B04-5AD7-411F-A5AC-CA038EC515D7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Spellchecking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handwriting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，依次输入以下四句命令，获取语言列表，筛选我们要删除的语言，删除筛选出的语言，设置回列表。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LangList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-WinUserLanguageList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ToDeletedLang&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LangList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LanguageTag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-eq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zh-Hans-CN&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LangList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ToDeletedLang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Set-WinUserLanguageList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LangList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;删除下载缓存文件&quot;&gt;删除下载缓存文件&lt;/h3&gt;

&lt;p&gt;进入文件夹“C:\Windows\SoftwareDistribution\Download”，删除里面的所有文件和文件夹。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-13-30-29.png&quot; alt=&quot;删除下载缓存&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，重新启动计算机。&lt;/p&gt;

&lt;h3 id=&quot;重新下载中文语言包&quot;&gt;重新下载中文语言包&lt;/h3&gt;

&lt;p&gt;现在，按照正常的安装中文语言包的方式再安装一次语言包。&lt;/p&gt;

&lt;p&gt;即“Settings -&amp;gt; Time &amp;amp; Language -&amp;gt; Language -&amp;gt; Add a preferred language”，然后选择中文，下一步 -&amp;gt; 安装。等待安装结束。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-13-33-32.png&quot; alt=&quot;选中中文&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等安装进度条全部结束之后，再选择现实语言为“中文”即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-02-07-13-35-17.png&quot; alt=&quot;选择现实语言为中文&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 23 Mar 2020 03:35:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-chaotic-language-of-windows-10-settings.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-chaotic-language-of-windows-10-settings.html</guid>
        
        
        <category>windowes</category>
        
      </item>
    
      <item>
        <title>使用 SoftEther VPN 在 VPS 和个人电脑之间搭建 VPN 网络</title>
        <description>&lt;p&gt;VPN 全称是 Virtual Private Network（虚拟专用网络），可以在多台设备之间建立安全的通信网络。VPS 是 Virtual Private Server，虚拟专用服务器，指的是一台虚拟的电脑，用于提供服务。&lt;/p&gt;

&lt;p&gt;注意，本文不会谈及科学上网相关的内容。本文建立 VPN 网络单纯是为了对外提供服务。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;vpn-虚拟专用网络&quot;&gt;VPN 虚拟专用网络&lt;/h2&gt;

&lt;h3 id=&quot;为什么我们需要一个-vps&quot;&gt;为什么我们需要一个 VPS&lt;/h3&gt;

&lt;p&gt;为了对公众提供服务，我们需要：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一台 24×7 小时不关机的计算机&lt;/li&gt;
  &lt;li&gt;一个可被互联网访问到的 IP（公网 IP）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对个人家用电脑来说，你可以做到 24 小时不关机，但你拿不到一个稳定的公网 IP。中国电信可以在办网的时候提供公网 IP，但此 IP 会经常改变，因此几乎无法对外提供服务。虽然可以使用 DDNS（动态域名解析服务），但因为域名解析存在缓存，所以当 IP 改变的时候，你会有数分钟到数小时不等的时间无法访问到正确的 IP。&lt;/p&gt;

&lt;p&gt;因此，个人电脑是无法稳定对外提供服务的——&lt;strong&gt;我们需要一个 VPS&lt;/strong&gt;——它有固定的公网 IP。&lt;/p&gt;

&lt;h3 id=&quot;反向代理&quot;&gt;反向代理&lt;/h3&gt;

&lt;p&gt;实际上，只需要一个 VPS 我们就能直接对外提供服务了——将服务部署到 VPS 上就可以。但我们也可以将服务部署到另一台计算机上，甚至这台计算机可以没有公网 IP。于是 VPS 上只需要部署一个反向代理服务器即可。&lt;/p&gt;

&lt;p&gt;如果使用 FRP 这种反向代理服务器，那么不需要固定公网 IP 就能反向代理。然而 FRP 也有一些不能满足的要求，这时部署反向代理服务器，我们需要真实提供服务的计算机也具有固定的 IP。而 VPN 网络可以提供这一点。&lt;/p&gt;

&lt;h3 id=&quot;虚拟专用网络vpn&quot;&gt;虚拟专用网络（VPN）&lt;/h3&gt;

&lt;p&gt;如果我们将 VPS 和其他散布在内网或者非固定 IP 的公网计算机连接起来，组成一个专用的网络，那么这个就是“虚拟专用网络”（VPN）。这样，无论这些电脑散布在哪些地方，在哪些网络中，对于对方来说都是“内网”中的另一台电脑，这是可以有固定（内网）IP 的，于是可以做很多事情。&lt;/p&gt;

&lt;p&gt;我画了一张简单的图来描述一个简单的 VPN 网络。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-29-23-09-12.png&quot; alt=&quot;虚拟专用网络&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来，本文将介绍如何搭建一个如图所示的 VPN 网络。&lt;/p&gt;

&lt;h2 id=&quot;vps-端---服务端&quot;&gt;VPS 端 - 服务端&lt;/h2&gt;

&lt;p&gt;以 Debian 系统的 VPS 为例，除了包管理工具和文本编辑工具，其他各种操作都是大同小异。&lt;/p&gt;

&lt;p&gt;我们要在 VPS 端安装一个 SoftEther 的服务端和一个客户端。服务端用于连接整个 VPN 网络，而客户端用于将此 VPS 主机组成此 VPN 网络中的第一台计算机。&lt;/p&gt;

&lt;h3 id=&quot;下载-softether-vpn&quot;&gt;下载 SoftEther VPN&lt;/h3&gt;

&lt;p&gt;下载地址&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.softether.org/download&quot;&gt;Download - SoftEther VPN Project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载有很多的种类可选。考虑到我们会部署到多台计算机上，所以建议选择最末尾的一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ZIP CD-ROM Image Package of SoftEther VPN&lt;/code&gt;。这样，我们可以从一个文件夹中取出所有我们想要的运行环境。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-16-33-11.png&quot; alt=&quot;选择最大的一个下载&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;安装-softether-vpn-server&quot;&gt;安装 SoftEther VPN Server&lt;/h3&gt;

&lt;p&gt;在安装 SoftEther VPN 的服务端之前，我们需要确保你的 Linux 系统上有这些工具：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;make&lt;/li&gt;
  &lt;li&gt;gcc&lt;/li&gt;
  &lt;li&gt;net-tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果是 Debian 系统，可以运行命令安装：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;make
apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;gcc
apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;net-tools
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来的安装过程有编译步骤，所以需要用到以上工具。&lt;/p&gt;

&lt;p&gt;将我们下载下来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\VPN-CD-v4.30-9696-beta-2019.07.08\Linux\SoftEther_VPN_Server\64bit_-_Intel_x64_or_AMD64\softether-vpnserver-v4.30-9696-beta-2019.07.08-linux-x64-64bit\vpnserver&lt;/code&gt; 文件夹上传到 Linux 系统中，建议 &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/SoftEther&lt;/code&gt; 目录，如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-20-26-51.png&quot; alt=&quot;上传 vpnserver 文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，进入 vpnserver 文件夹，运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;.install.sh&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;vpnserver
bash .install.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-20-33-49.png&quot; alt=&quot;运行 .install.sh&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置-softether-vpn-server-服务&quot;&gt;配置 SoftEther VPN Server 服务&lt;/h3&gt;

&lt;p&gt;现在，前往 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 目录，这个目录存放了各种系统服务的启用和停用脚本。我们需要将 VPN Server 服务放到这个目录下。&lt;/p&gt;

&lt;p&gt;你可以在官方文档 &lt;a href=&quot;https://www.softether.org/4-docs/1-manual/7._Installing_SoftEther_VPN_Server/7.3_Install_on_Linux_and_Initial_Configurations&quot;&gt;7.3 Install on Linux and Initial Configurations - SoftEther VPN Project&lt;/a&gt; 找到服务脚本。&lt;/p&gt;

&lt;p&gt;记得将下面 DAEMON 中的路径改成你自己的路径。改好之后，命名成 &lt;code class=&quot;highlighter-rouge&quot;&gt;vpnserver&lt;/code&gt;，放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 目录下。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# chkconfig: 2345 99 01&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# description: SoftEther VPN Server&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DAEMON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/SoftEther/vpnserver/vpnserver
&lt;span class=&quot;nv&quot;&gt;LOCK&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lock/subsys/vpnserver
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in
&lt;/span&gt;start&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; start
&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$LOCK&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
stop&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; stop
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$LOCK&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
restart&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; stop
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;3
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; start
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; {start|stop|restart}&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;esac
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请确保此文件的行尾符是 CL，而不是 Windows 系统中的 CLRF。&lt;/p&gt;

&lt;p&gt;将此文件放入 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 后，记得修改文件的属性：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 vpnserver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-21-15-32.png&quot; alt=&quot;准备好服务&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置-softether-vpn-server-开机启动&quot;&gt;配置 SoftEther VPN Server 开机启动&lt;/h3&gt;

&lt;p&gt;Ubuntu 或者 Debian 系统的开机启动项脚本位于 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/rcN.d/&lt;/code&gt; 目录中，我们可以使用以下命令设置其开机启动。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;update-rc.d vpnserver defaults
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;启动-softether-vpn-server&quot;&gt;启动 SoftEther VPN Server&lt;/h3&gt;

&lt;p&gt;使用以下命令启动服务：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/etc/init.d/vpnserver start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;vps-端---客户端&quot;&gt;VPS 端 - 客户端&lt;/h2&gt;

&lt;p&gt;VPS 主机也需要安装运行 SoftEther VPN 客户端，这样这台主机才会成为 VPN 网络的其中一台主机。&lt;/p&gt;

&lt;p&gt;安装方法与服务端非常类似，所以下面的介绍会简略一些。&lt;/p&gt;

&lt;h3 id=&quot;安装-softether-vpn-client&quot;&gt;安装 SoftEther VPN Client&lt;/h3&gt;

&lt;p&gt;将我们下载下来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\VPN-CD-v4.30-9696-beta-2019.07.08\Linux\SoftEther_VPN_Client\64bit_-_Intel_x64_or_AMD64\softether-vpnclient-v4.30-9696-beta-2019.07.08-linux-x64-64bit\vpnclient&lt;/code&gt; 文件夹上传到 Linux 系统中，建议 &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/SoftEther&lt;/code&gt; 目录，如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-21-44-22.png&quot; alt=&quot;上传 vpnclient 文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，进入 vpnclient 文件夹，运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;.install.sh&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;vpnclient
bash .install.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-20-33-49.png&quot; alt=&quot;运行 .install.sh&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置-softether-vpn-client-服务&quot;&gt;配置 SoftEther VPN Client 服务&lt;/h3&gt;

&lt;p&gt;现在，前往 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 目录，这个目录存放了各种系统服务的启用和停用脚本。我们需要将 VPN Client 服务放到这个目录下。&lt;/p&gt;

&lt;p&gt;记得将下面 DAEMON 中的路径改成你自己的路径。改好之后，命名成 &lt;code class=&quot;highlighter-rouge&quot;&gt;vpnclient&lt;/code&gt;，放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 目录下。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# chkconfig: 2345 99 01&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# description: SoftEther VPN Client&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DAEMON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/SoftEther/vpnclient/vpnclient
&lt;span class=&quot;nv&quot;&gt;LOCK&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lock/subsys/vpnclient
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in
&lt;/span&gt;start&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; start
&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$LOCK&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
stop&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; stop
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$LOCK&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
restart&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; stop
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;3
&lt;span class=&quot;nv&quot;&gt;$DAEMON&lt;/span&gt; start
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; {start|stop|restart}&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;esac
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将此文件放入 &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d&lt;/code&gt; 后，记得修改文件的属性：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 vpnclient
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;配置-softether-vpn-client-开机启动&quot;&gt;配置 SoftEther VPN Client 开机启动&lt;/h3&gt;

&lt;p&gt;使用以下命令设置其开机启动。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;update-rc.d vpnclient defaults
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;启动-softether-vpn-client&quot;&gt;启动 SoftEther VPN Client&lt;/h3&gt;

&lt;p&gt;使用以下命令启动服务：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/etc/init.d/vpnclient start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动后，会出现一些提示：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The SoftEther VPN Server service has been started.

Let&lt;span class=&quot;s1&quot;&gt;'s get started by accessing to the following URL from your PC:

https://**.**.**.**:5555/
  or
https://**.**.**.**/

Note: IP address may vary. Specify your server'&lt;/span&gt;s IP address.
A TLS certificate warning will appear because the server uses self signed certificate by default. That is natural. Continue with ignoring the TLS warning.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里会提示你 VPN 服务器的 IP 和端口号。我们接下来在配置客户端的时候会用到这个 IP 和端口号。&lt;/p&gt;

&lt;h2 id=&quot;个人电脑端&quot;&gt;个人电脑端&lt;/h2&gt;

&lt;p&gt;这里个人电脑端我们使用 Windows 系统。&lt;/p&gt;

&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;

&lt;p&gt;将我们下载下来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\VPN-CD-v4.30-9696-beta-2019.07.08\Windows\SoftEther_VPN_Server_and_VPN_Bridge安装服务端管理工具&lt;/code&gt; 文件夹打开，运行里面的 exe 安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-13-52-03.png&quot; alt=&quot;安装服务端管理工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 我们将用这个工具来管理我们在 VPS 上部署的 VPN Server&lt;/p&gt;

&lt;p&gt;将我们下载下来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\VPN-CD-v4.30-9696-beta-2019.07.08\Windows\SoftEther_VPN_Client&lt;/code&gt; 文件夹打开，运行里面的 exe 安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-21-55-55.png&quot; alt=&quot;安装 Windows 端&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-10-23-22.png&quot; alt=&quot;安装管理工具和客户端&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 我们将用这个工具来管理我们在 VPS 上部署的 VPN Client 以及在本机上部署的 VPN Client&lt;/p&gt;

&lt;h3 id=&quot;配置-vps-上的-vpn-server&quot;&gt;配置 VPS 上的 VPN Server&lt;/h3&gt;

&lt;p&gt;启动“SE-VPN Server Manager (Tools)”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-13-55-55.png&quot; alt=&quot;SE-VPN Server Manager (Tools)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们将使用此工具配置我们在 VPS 上的 VPN Server。&lt;/p&gt;

&lt;p&gt;第一步：设置新连接，输入设置名、主机名和端口（就是我面前面在 VPS 上输出的 IP 和端口）。输入完之后点确定。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-05-46.png&quot; alt=&quot;设置设置名、主机名和端口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二步：连接这个设置，第一次连接会提示设置管理员密码，请自己设置一个。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-11-15.png&quot; alt=&quot;设置管理员密码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第三步：安装和配置 VPN Server&lt;/p&gt;

&lt;p&gt;设置过程是一步步来的，你可以考虑按照下图依次设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-09-34.png&quot; alt=&quot;安装 VPN Server&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 远程访问 VPN Server&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-13-42.png&quot; alt=&quot;动态 DNS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 设置好主机名后，直接点退出&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-17-41.png&quot; alt=&quot;IPsec 设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 启用 L2TP 服务器功能，并设置好 IPsec 预共享密钥&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-19-21.png&quot; alt=&quot;VPN Azure 服务设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 禁用 VPN Azure&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-14-22-42.png&quot; alt=&quot;创建新用户&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 创建新用户&lt;/p&gt;

&lt;p&gt;至此，服务端就设置完毕。接下来我们设置客户端。&lt;/p&gt;

&lt;!-- 启动 SoftEther VPN 命令行实用工具(vpncmd)。

![SoftEther VPN 命令行实用工具(vpncmd)](/static/posts/2020-01-31-11-19-49.png)

我们需要配置 VPN Server 的密码。按下图，我们依次选择“VPN Server 或 VPN Bridge 的管理”，VPN Server 的 IP 地址。在最后问 HUB 名称的时候，因为我们没有创建，所以直接回车（不用输入）。

这时，会进入 VPN Server 控制台。

![进入 VPN Server 控制台](/static/posts/2020-01-31-11-19-00.png) --&gt;

&lt;h3 id=&quot;配置本地的-vpn-client&quot;&gt;配置本地的 VPN Client&lt;/h3&gt;

&lt;p&gt;启动 SoftEther VPN Client 管理工具，我们即将使用此工具管理本机的客户端和刚刚配好的 VPS 主机上的客户端。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-10-26-11.png&quot; alt=&quot;启动 SoftEther VPN Client 管理工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“添加新的 VPN 连接”。首次点击的时候会提示创建虚拟网络适配器，点击“是”让它创建即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-10-38-15.png&quot; alt=&quot;创建虚拟网络适配器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-10-38-42.png&quot; alt=&quot;正在创建虚拟网络适配器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，添加新的 VPN 连接：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-16-20-23.png&quot; alt=&quot;添加新的 VPN 连接&quot; /&gt;&lt;/p&gt;

&lt;p&gt;设置连接信息。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;连接设置名：随便填写&lt;/li&gt;
  &lt;li&gt;主机名：前面我们启动 VPN Server 时输出的 IP&lt;/li&gt;
  &lt;li&gt;端口号：前面我们启动 VPN Server 时输出的端口号&lt;/li&gt;
  &lt;li&gt;虚拟 HUB 名：前面我们填完之后，这里就会自动出现了，选择即可&lt;/li&gt;
  &lt;li&gt;用户认证设置：我们前一步设置用户的时候设置的用户名和密码&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-16-22-56.png&quot; alt=&quot;设置连接信息&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置-vps-上的-vpn-client&quot;&gt;配置 VPS 上的 VPN Client&lt;/h3&gt;

&lt;p&gt;启动“管理远程电脑上的 SoftEther VPN Client”程序：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-16-37-26.png&quot; alt=&quot;管理远程电脑上的 SoftEther VPN Client&quot; /&gt;&lt;/p&gt;

&lt;p&gt;输入我们前面 VPS 上的 IP 地址：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-31-16-38-42.png&quot; alt=&quot;输入计算机名&quot; /&gt;&lt;/p&gt;

&lt;p&gt;……&lt;/p&gt;

&lt;!-- 
---

**参考资料** --&gt;
</description>
        <pubDate>Mon, 23 Mar 2020 03:35:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/build-vpn-on-vps-using-soft-ether.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/build-vpn-on-vps-using-soft-ether.html</guid>
        
        
        <category>linux</category>
        
        <category>network</category>
        
      </item>
    
      <item>
        <title>推荐 .NET/C# 开发者安装的几款代码分析插件或对应的代码分析 NuGet 包</title>
        <description>&lt;p&gt;如果你使用的是旧版本的 Visual Studio，那么默认的代码分析规则集是“最小建议规则集”。基于这个，写出来的代码其实只能说是能跑通过而已。随着 Roslyn 的发布，带来了越来越多更强大的代码分析器，可以为编写高质量的代码带来更多的帮助。&lt;/p&gt;

&lt;p&gt;作为 .NET/C# 开发者，强烈建议安装本文推荐的几款代码分析器。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;推荐&quot;&gt;推荐&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Visual Studio 2019 自带的分析器&lt;/li&gt;
  &lt;li&gt;Microsoft Code Analysis
    &lt;ul&gt;
      &lt;li&gt;VS 扩展：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=VisualStudioPlatformTeam.MicrosoftCodeAnalysis2019&quot;&gt;Microsoft Code Analysis 2019&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;NuGet 包： &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Roslynator
    &lt;ul&gt;
      &lt;li&gt;VS 扩展：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=josefpihrt.Roslynator2019&quot;&gt;Roslynator 2019&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;VS Code 扩展：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=josefpihrt-vscode.roslynator&quot;&gt;Roslynator&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;NuGet 包：&lt;a href=&quot;https://www.nuget.org/packages/Roslynator.Analyzers/&quot;&gt;Roslynator.Analyzers&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Code Cracker
    &lt;ul&gt;
      &lt;li&gt;VS 扩展：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=GiovanniBassi-MVP.CodeCrackerforC&quot;&gt;Code Cracker for C#&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;NuGet 包：&lt;a href=&quot;https://www.nuget.org/packages/codecracker.CSharp/&quot;&gt;codecracker.CSharp&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Meziantou.Analyzer
    &lt;ul&gt;
      &lt;li&gt;VS 扩展：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Meziantou.Meziantou-Analyzer&quot;&gt;Meziantou.Analyzer&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;NuGet 包：&lt;a href=&quot;https://www.nuget.org/packages/Meziantou.Analyzer/&quot;&gt;Meziantou.Analyzer&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;类型&quot;&gt;类型&lt;/h2&gt;

&lt;p&gt;这里的分析器分为 Visual Studio 扩展形式的分析器和 NuGet 包形式的分析器。&lt;/p&gt;

&lt;p&gt;Visual Studio 扩展形式的分析器可以让你一次安装对所有项目生效，但缺点是不能影响编译过程，只能作为在 Visual Studio 中编写代码时给出提示。&lt;/p&gt;

&lt;p&gt;NuGet 包形式的分析器可以让某个项目中的所有成员享受到同样的代码分析提示（无论是否安装插件），但缺点是仅针对单个项目生效。&lt;/p&gt;

&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;h3 id=&quot;visual-studio-2019-自带的分析器&quot;&gt;Visual Studio 2019 自带的分析器&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-10-19-55.png&quot; alt=&quot;重构提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-10-20-39.png&quot; alt=&quot;IDE0051&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图生效的分析器就是 Visual Studio 2019 自带的分析器。在可能有问题的代码上，Visual Studio 的代码编辑器会显示一些文字效果来提醒你代码问题。比如这张图就是提示私有成员 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 未使用。&lt;/p&gt;

&lt;p&gt;Visual Studio 2019 自带的分析器的诊断 ID 都是以 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDE&lt;/code&gt; 开头，因此你可以通过这个前缀来区分是否是 Visual Studio 2019 自带的分析器提示的。&lt;/p&gt;

&lt;p&gt;另外，自带的分析器可谓非常强大，除了以上这种提示之外，还可以提示一些重复代码的修改。比如你修改了某段代码，它会提示你相似的代码也可能需要修改。&lt;/p&gt;

&lt;h3 id=&quot;microsoft-code-analysis&quot;&gt;Microsoft Code Analysis&lt;/h3&gt;

&lt;p&gt;Microsoft Code Analysis 分为两种用法，一个是 Visual Studio 扩展的形式，你可以&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=VisualStudioPlatformTeam.MicrosoftCodeAnalysis2019&quot;&gt;去这里&lt;/a&gt;下载安装或者去 Visual Studio 的扩展管理界面搜索安装；另一个是 NuGet 包的形式，你可以直接在项目的 NuGet 管理界面安装 &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这款分析器也是微软主推的代码分析器，可以分析 API 设计问题、全球化与本地化问题、稳定性问题、性能问题、安全性问题、代码使用问题等非常多的种类。&lt;/p&gt;

&lt;p&gt;比如下图是稳定性的一个问题，直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 基类：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-10-35-47.png&quot; alt=&quot;catch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-10-35-56.png&quot; alt=&quot;配置提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;虽然你可以通过配置规则严重性来消除提示，但是这样写通常代码也比较容易出现一些诡异的问题而难以定位。&lt;/p&gt;

&lt;p&gt;Microsoft Code Analysis 分析器的诊断 ID 都是以 &lt;code class=&quot;highlighter-rouge&quot;&gt;CA&lt;/code&gt; 开头，因此你可以通过这个前缀来区分是否是 Microsoft Code Analysis 分析器提示的。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt; 的 NuGet 包实际上是一组分析器的合集，包括：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;主分析器，分析各种代码问题&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeQuality.Analyzers&quot;&gt;Microsoft.CodeQuality.Analyzers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;专门分析代码质量的分析器（比如没有使用某个参数）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.NetCore.Analyzers&quot;&gt;Microsoft.NetCore.Analyzers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;如果你在使用 .NET Core 或者 .NET Standard，那么此分析器会告诉你更恰当地使用框架提供的 API（如果 API 恰好与 .NET Framework 桌面应用相同，那么 .NET Framework 桌面应用也因此受益）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.NetFramework.Analyzers&quot;&gt;Microsoft.NetFramework.Analyzers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;如果你在使用 .NET Framework 开发桌面应用，那么此分析器会告诉你更恰当地使用框架提供的 API&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你想安装这款 NuGet 包，并不需要特别去 NuGet 包管理器中安装，也不需要命令行，只需要去项目的属性页面，选择“安装”就好了。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-11-36-25.png&quot; alt=&quot;安装分析器&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;roslynator&quot;&gt;Roslynator&lt;/h3&gt;

&lt;p&gt;是第三方开发者开发的，代码已在 GitHub 上开源，社区非常活跃：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/JosefPihrt/Roslynator&quot;&gt;JosefPihrt/Roslynator: A collection of 500+ analyzers, refactorings and fixes for C#, powered by Roslyn.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;提供了 500 多个代码分析和重构。更值得推荐的一个原因是他为 Visual Studio 原本的很多报告了问题的代码提供了生成解决问题代码的能力。&lt;/p&gt;

&lt;h3 id=&quot;code-cracker&quot;&gt;Code Cracker&lt;/h3&gt;

&lt;p&gt;Code Cracker 是第三方开发者开发的，代码已在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/code-cracker/code-cracker&quot;&gt;code-cracker/code-cracker: An analyzer library for C# and VB that uses Roslyn to produce refactorings, code analysis, and other niceties.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于这款分析器的出现比 Visual Studio 2019 早很多，所以待 Visual Studio 2019 出现的时候，他们已经出现了一些规则的重复（意味着你可能同一个问题会被 Visual Studio 报一次，又被 Code Cracker 报一次）。&lt;/p&gt;

&lt;p&gt;虽然部分重复，但 Code Cracker 依然提供了很多 Visual Studio 2019 和 Microsoft Code Analysis 都没有带的代码质量提示。&lt;/p&gt;

&lt;p&gt;比如，如果你代码中的文档注释缺少了某个参数的注释，那么它会给出提示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-10-43-03.png&quot; alt=&quot;CC0097&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Code Cracker 支持的所有种类的代码分析都可以在这里查得到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://code-cracker.github.io/diagnostics.html&quot;&gt;All diagnostics - Code Cracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;meziantouanalyzer&quot;&gt;Meziantou.Analyzer&lt;/h3&gt;

&lt;p&gt;这款插件是对其他几款分析器的重要补充。如果说其他几款分析器可以帮你解决一些基本设计问题或者 Bug 的话，这款分析器可以帮你发现更大范围的问题。&lt;/p&gt;

&lt;p&gt;最典型的，也是我推荐这款分析器的最大原因是 —— 区域和本地化！&lt;/p&gt;

&lt;p&gt;你的每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt;，每一个字符串比较，每一个字典的构造……他都提醒你需要考虑区域问题，然后提供给你区域问题的推荐代码！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-21-15-56-05.png&quot; alt=&quot;提醒需要考虑区域问题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-21-15-56-20.png&quot; alt=&quot;提供的建议&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;配置代码分析严重程度&quot;&gt;配置代码分析严重程度&lt;/h2&gt;

&lt;p&gt;你的项目中对于某项规则严重性的看法也许跟微软或其他第三方分析器不一样，因此你需要自己配置规则集的严重性。&lt;/p&gt;

&lt;p&gt;关于如何配置代码分析严重程度，你可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/use-editor-config-file-to-config-diagnostic-severities&quot;&gt;使用 .editorconfig 配置 .NET/C# 项目的规则严重性 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 23 Mar 2020 03:31:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/recommend-some-code-analysis-extensions-or-nuget-packages.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/recommend-some-code-analysis-extensions-or-nuget-packages.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>用命令行执行 .NET 单元测试时，如何仅执行符合某些条件的单元测试</title>
        <description>&lt;p&gt;本文介绍使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet test&lt;/code&gt; 命令进行单元测试的时候，过滤出被测项目中的一部分测试出来，仅测试这一部分。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;建一个 .NET Core 的单元测试项目，例如项目名字是 Walterlv.Demo.Tests。举例其中的一个测试类如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Tests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooTest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestCategory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CategoryA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestMethod1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestMethod2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用 Visual Studio 的话，直接在测试资源管理器中点击运行全部测试，或者选择想要测试的项点运行所选测试即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-11-16-43-44.png&quot; alt=&quot;Visual Studio 测试资源管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而使用 GUI 工具的话不利于 CI 集成和自动化测试，所以必然需要用到命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Demo.Tests.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有时为了调试方便或输出分类数据等，要求执行一部分单元测试，这就需要过滤了。&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet test&lt;/code&gt; 的过滤使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;--filter&lt;/code&gt; 选项。&lt;/p&gt;

&lt;h2 id=&quot;过滤&quot;&gt;过滤&lt;/h2&gt;

&lt;h3 id=&quot;方法名&quot;&gt;方法名&lt;/h3&gt;

&lt;p&gt;查找方法名包含某字符串的单元测试并执行：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestMethod1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果是排除某方法，则是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullyQualifiedName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;类名&quot;&gt;类名&lt;/h3&gt;

&lt;p&gt;查找类名等于某字符串的单元测试并执行：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooTest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;类名必须包含命名空间，否则找不到。&lt;/p&gt;

&lt;h3 id=&quot;分类与优先级&quot;&gt;分类与优先级&lt;/h3&gt;

&lt;p&gt;查找标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;[TestCategory(&quot;CategoryA&quot;)]&lt;/code&gt; 的方法并执行单元测试：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestCategory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CategoryA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;查找标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Priority(2)]&lt;/code&gt; 的方法并执行单元测试：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;条件与或&quot;&gt;条件与或&lt;/h3&gt;

&lt;p&gt;条件或（&lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt;）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCategory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CategoryA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;条件与（’&amp;amp;’）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCategory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CategoryA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests&quot;&gt;Running selective unit tests - .NET Core - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 11 Mar 2020 09:59:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-test-with-filter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-test-with-filter.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET WebClient 类下载部分文件会错误？可能是解压缩的锅</title>
        <description>&lt;p&gt;一直在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 下载文件，.NET 已经封装好，所以用起来代码非常简洁；但直到今天发现有一个文件一直不能正确下载下来。&lt;/p&gt;

&lt;p&gt;本文介绍这个问题的原因和解决方法，更重要的是给出调查方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;本文所涉及到的域名已经过敏感信息处理，所以实际上你是无法访问到的；但这不影响本文对调查方法的描述。&lt;/p&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;我原本是使用如下的代码去下载任意文件的（参数经过简化）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DownloadFileAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:5000/walterlv-icon.svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\lvyi\Desktop\TEST\walterlv-icon.svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;webClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WebClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;webClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，下载一个 svg 的时候，原本应该是如下的图片：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-walterlv-icon.svg&quot; alt=&quot;walterlv-icon.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而实际上下载下来之后却是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-03-walterlv-icon-downloaded.svg&quot; alt=&quot;walterlv-icon.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原本大小是 992 字节，实际下载下来后是 508 字节，而且固定是 508 字节。你可以通过右键复制图片地址，然后分别把两张图下载下来看。&lt;/p&gt;

&lt;h2 id=&quot;调查&quot;&gt;调查&lt;/h2&gt;

&lt;p&gt;显然，&lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 没有抛出任何异常，而且每次下载下来都是固定的 508 字节，说明肯定不是网络不通或程序提前退出导致的，也不是线程安全相关的问题。基本可以认定为问题出在服务器的配置，或者客户端的请求上。&lt;/p&gt;

&lt;h3 id=&quot;使用其他正常下载器尝试&quot;&gt;使用其他“正常”下载器尝试&lt;/h3&gt;

&lt;p&gt;拿 Chrome 跑以上地址，拿专用下载工具跑以上地址，甚至是拿 Postman 跑以上地址，都可以成功显示或者下载到正确的图片。&lt;/p&gt;

&lt;p&gt;这几乎可以肯定，问题出在 .NET 的 WebClient 上，可能是请求不对，或者对响应的后续处理不对。&lt;/p&gt;

&lt;h3 id=&quot;使用-postman-和-webclient-对比测试&quot;&gt;使用 Postman 和 WebClient 对比测试&lt;/h3&gt;

&lt;p&gt;为了对比请求和响应，我使用的是 Fiddler 抓包。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 请求：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;GET http://localhost:5000/walterlv-icon.svg HTTP/1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost:5000&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Keep-Alive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 响应（因为内含乱码，会让网页显示不正常，所以放截图）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-11-17-01-05.png&quot; alt=&quot;WebClient 响应&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Postman 请求：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;GET http://localhost:5000/walterlv-icon.svg HTTP/1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;User-Agent&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PostmanRuntime/7.22.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/*&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Cache-Control&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;no-cache&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Postman-Token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;05bb3d80-d7a7-4c0d-bdd1-9cd65d79ecab&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost:5000&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gzip, deflate, br&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keep-alive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Postman 响应（因为内含乱码，会让网页显示不正常，所以放截图）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-03-11-17-02-13.png&quot; alt=&quot;Postman 响应&quot; /&gt;&lt;/p&gt;

&lt;p&gt;请求和响应贴得很长，这可以让比较感兴趣的小伙伴仔细比较。但这里我直接给出我比较后的结论：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Postman 的请求会发送比较多的头&lt;/li&gt;
  &lt;li&gt;两者的响应几乎相同（包括文件大小和内容）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;由于响应几乎相同，所以实际上前面请求头的不同可以忽略了（至少说明返回的内容没有因为请求的不同而有所变化），我们能够拿到完整的整个文件。&lt;/p&gt;

&lt;p&gt;那么问题基本确定就是在 WebClient 对这个响应的处理上了。&lt;/p&gt;

&lt;p&gt;可以注意到 Postman 的请求中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Accept-Encoding&lt;/code&gt;，两折的响应中都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Encoding&lt;/code&gt;，指定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;gzip&lt;/code&gt;。然而这是 Linux 中用来压缩文件的命令。响应中指定了内容编码方式为 &lt;code class=&quot;highlighter-rouge&quot;&gt;gzip&lt;/code&gt; 是否意味着我们下载下来的文件实际上是一个 gzip 压缩文件呢？&lt;/p&gt;

&lt;p&gt;于是我将下载下来的文件扩展名改为 gzip，用压缩文件打开，于是真的可以解压出来真实的图片。&lt;/p&gt;

&lt;p&gt;于是确认问题的原因是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 在处理响应的时候没有根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Encoding&lt;/code&gt; 的值解压缩下载下来的文件。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;解决的思路：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 支持下载文件后解压缩
&lt;!-- 2. 不要使用 `WebClient` 下载 --&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;使-webclient-支持下载文件后解压缩&quot;&gt;使 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 支持下载文件后解压缩&lt;/h3&gt;

&lt;p&gt;各种检查后发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 竟然没有提供设置解压缩相关的属性。庆幸的是，在网上搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;gzip&lt;/code&gt; 关键字后，找到了这一篇答案：&lt;a href=&quot;https://stackoverflow.com/a/4914874/6233938&quot;&gt;.net - Automatically decompress gzip response via WebClient.DownloadData - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我们需要重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebClient.GetWebRequest&lt;/code&gt; 方法，然后改写 &lt;code class=&quot;highlighter-rouge&quot;&gt;AutomaticDecompression&lt;/code&gt; 属性。此属性可以改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;gzip&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;deflate&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;br&lt;/code&gt; 或者它们的组合，这与 Postman 发请求时声明支持的值是完全一样的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AutoDecompressionWebClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebClient&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebRequest&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWebRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWebRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseRequest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpWebRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpWebRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;httpWebRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutomaticDecompression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DecompressionMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，也可以在拉取到响应的流后自己去做解压，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/16856/6233938&quot;&gt;.net - How do you download and extract a gzipped file with C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/4914874/6233938&quot;&gt;.net - Automatically decompress gzip response via WebClient.DownloadData - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19227156/webclient-downloadfile-file-corrupt&quot;&gt;c# - WebClient.DownloadFile File Corrupt - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Questions/620959/Download-file-using-Webclient-shows-Wrong-Data&quot;&gt;Download file using Webclient shows Wrong Data - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Questions/604484/WebClient-DownloadFileplusdownloadsplusdamagedplus&quot;&gt;[Solved] WebClient DownloadFile method downloads damaged PDF files - CodeProject&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 11 Mar 2020 09:02:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-webclient-decompression-when-downloaded.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-webclient-decompression-when-downloaded.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何在终端和 PowerShell 中将一个命令自动重复执行多次</title>
        <description>&lt;p&gt;你可能有很多原因要将一个命令重复执行多次，本文介绍在多个平台下如何多次重复执行命令。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;最近遇到一个偶然复现的单元测试错误，于是在每次运行时，打算重复运行多次来调查问题是否已经解决。&lt;/p&gt;

&lt;p&gt;实际上，重复执行命令有更多用途：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;通过重复执行来复现一些偶发的问题&lt;/li&gt;
  &lt;li&gt;执行一组命令，每次只有参数不同&lt;/li&gt;
  &lt;li&gt;大大减轻手工输入命令的工作量&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;bash&quot;&gt;Bash&lt;/h2&gt;

&lt;p&gt;Linux 或者 Mac 系统的终端中，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 来完成重复执行命令，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;${}&lt;/code&gt; 来引用定义的变量。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;1..10&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这表示从 1 到 10（两端的值都会取到），依次输出这些数。&lt;/p&gt;

&lt;p&gt;比如，我们需要运行 100 次单元测试，那么：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv@localhost:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;1..100&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;dotnet &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; ./Walterlv.Tests.dll&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;Microsoft &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 测试执行命令行工具版本 16.3.0
版权所有 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Microsoft Corporation。保留所有权利。
正在启动测试执行，请稍候...

总共 1 个测试文件与指定模式相匹配。

测试运行成功。
测试总数: 238
     通过数: 238
总时间: 1.6384 秒
Microsoft &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 测试执行命令行工具版本 16.3.0
版权所有 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Microsoft Corporation。保留所有权利。
正在启动测试执行，请稍候...

总共 1 个测试文件与指定模式相匹配。

测试运行成功。
测试总数: 238
     通过数: 238
总时间: 1.7138 秒
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;powershell&quot;&gt;PowerShell&lt;/h2&gt;

&lt;p&gt;PowerShell Core 是跨平台的配置框架，可以在 Windows/Linux/Mac 系统下使用。在 PowerShell 中，也可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这表示从 1 到 10（两端的值都会取到），依次输出这些数。&lt;/p&gt;

&lt;p&gt;比如，我们需要运行 100 次单元测试，那么：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Tests.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试执行命令行工具版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;16.3.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;版权所有&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Corporation&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。保留所有权利。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在启动测试执行，请稍候&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;总共&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个测试文件与指定模式相匹配。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试运行成功。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试总数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;通过数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;总时间&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6384&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;秒&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试执行命令行工具版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;16.3.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;版权所有&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Corporation&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。保留所有权利。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在启动测试执行，请稍候&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;总共&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个测试文件与指定模式相匹配。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试运行成功。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;测试总数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;通过数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;总时间&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.7138&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;秒&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/better-programming/how-to-run-a-command-multiple-times-in-terminal-and-powershell-5af76df8d123&quot;&gt;How To Run a Command Multiple Times in Terminal and PowerShell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 11 Mar 2020 08:26:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/run-a-command-multiple-times-in-terminal-and-powershell.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/run-a-command-multiple-times-in-terminal-and-powershell.html</guid>
        
        
        <category>windows</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>Linux 系统根目录下的文件夹</title>
        <description>&lt;p&gt;本文介绍 Linux 系统根目录下的各种文件夹及其用途，了解这些目录可以帮助你更好地管理你的 Linux 主机。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;linux-系统根目录&quot;&gt;Linux 系统根目录&lt;/h2&gt;

&lt;p&gt;各个不同 Linux 发行版的根目录会有一些区别，但大多数发行版的主要的目录都是有的。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/bin&lt;/code&gt; binary 用于存放经常使用的命令&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot&lt;/code&gt; boot 启动时的一些核心文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/dev&lt;/code&gt; device 外部设备&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/etc&lt;/code&gt; 用于存放各种系统配置和管理配置（名字来源于法语 et cetera，意思就是 etc…，表示还有一些其他的东西等等，其实就是指一堆杂项，不过现在就用来存放一堆配置文件了）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/home&lt;/code&gt; 用户目录，里面按用户名命名了子文件夹&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/lib&lt;/code&gt; library 存放系统最基本的动态链接共享库&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/lib64&lt;/code&gt; library 64bit 动态链接库的 64 位版本&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/lost+found&lt;/code&gt; 一般情况下是空的，但在非法关闭后，这里就会存放一些文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/media&lt;/code&gt; 识别出的 U 盘，光驱等会在这个目录下&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/mnt&lt;/code&gt; mount 系统提供此文件夹用于给用户挂载其他的文件系统，例如光驱&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/opt&lt;/code&gt; 用于安装软件的目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/proc&lt;/code&gt; 是一个虚拟目录，是系统的内存映射，可通过访问此目录获取系统信息（这个目录的内容不在硬盘上而在内存里）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/root&lt;/code&gt; 超级管理员 root 用户的主目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/run&lt;/code&gt; 用于在系统启动时运行的程序&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/sbin&lt;/code&gt; super binary 系统超集管理员使用的系统管理程序&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/srv&lt;/code&gt; service 存放一些服务启动之后需要提取的数据。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/sys&lt;/code&gt; 存放 Linux 系统内核文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; 用于存放一些临时文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/usr&lt;/code&gt; 用户的应用程序和文件都在此目录下，类似于 Windows 系统中的 Program Files 目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/var&lt;/code&gt; 经常被修改的文件可以放到这个目录，比如说日志文件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-30-16-00-42.png&quot; alt=&quot;Linux 系统根目录&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.runoob.com/linux/linux-system-contents.html&quot;&gt;Linux 系统目录结构 - 菜鸟教程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/mianjunan/article/details/6684966&quot;&gt;Linux中etc目录详解大全总汇详解_mianjunan的博客-CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Jan 2020 08:02:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/linux-root-directories.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/linux-root-directories.html</guid>
        
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>收集的 Linux VPS 在线重装系统脚本</title>
        <description>&lt;p&gt;因为 VPS 上预装的操作系统我并不习惯，所以打算重装一个。有的 VPS 服务商提供了较多种类的系统选择，有的却没有。如果你发现你希望重装的系统服务商没有提供，可以考虑自己安装。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重装脚本---来自萌咖&quot;&gt;重装脚本 - 来自萌咖&lt;/h2&gt;

&lt;p&gt;以下是来自萌咖的一键重装脚本的发布贴：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://moeclub.org/2017/03/25/82/?spm=79.7&quot;&gt;[ Linux VPS ] Debian(Ubuntu)网络安装/重装系统一键脚本 - 萌咖&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://moeclub.org/2018/03/26/597/&quot;&gt;[ Linux VPS ] CentOS 网络安装/重装系统一键脚本 纯净安装 - 萌咖&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;centos---debian&quot;&gt;CentOS -&amp;gt; Debian&lt;/h3&gt;

&lt;p&gt;第一步：先确保安装所需的软件。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; gawk &lt;span class=&quot;nb&quot;&gt;sed grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@walterlv ~]# yum &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; gawk &lt;span class=&quot;nb&quot;&gt;sed grep
&lt;/span&gt;Loaded plugins: fastestmirror
Determining fastest mirrors
epel/x86_64/metalink                                      | 9.5 kB  00:00:00
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; base: mirror.xtom.com.hk
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; epel: my.mirrors.thegigabit.com
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; extras: mirror.xtom.com.hk
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; updates: mirror.xtom.com.hk
base                                                                                                                                                                | 3.6 kB  00:00:00     
epel                                                                                                                                                                | 5.3 kB  00:00:00     
extras                                                                                                                                                              | 2.9 kB  00:00:00     
updates                                                                                                                                                             | 2.9 kB  00:00:00     
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: base/7/x86_64/group_gz                             | 165 kB  00:00:01
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: extras/7/x86_64/primary_db                         | 159 kB  00:00:00
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: epel/x86_64/group_gz                               |  90 kB  00:00:01
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: epel/x86_64/updateinfo                             | 1.0 MB  00:00:03
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;5/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: base/7/x86_64/primary_db                           | 6.0 MB  00:00:04
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: updates/7/x86_64/primary_db                        | 5.9 MB  00:00:03
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;7/7&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: epel/x86_64/primary_db                             | 6.9 MB  00:06:01
Package gawk-4.0.2-4.el7_3.1.x86_64 already installed and latest version
Package sed-4.2.2-5.el7.x86_64 already installed and latest version
Package grep-2.20-3.el7.x86_64 already installed and latest version
Nothing to &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第二步：下载脚本。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget &lt;span class=&quot;nt&quot;&gt;--no-check-certificate&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-qO&lt;/span&gt; DebianNET.sh &lt;span class=&quot;s1&quot;&gt;'https://moeclub.org/attachment/LinuxShell/DebianNET.sh'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x DebianNET.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第三步：全自动安装 Debian 9&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bash DebianNET.sh &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; 9 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 64 &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;-a&lt;/code&gt; 是自动安装。如果不指定或者指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;-m&lt;/code&gt; 则需要去 VNC 手动安装。&lt;/p&gt;

&lt;p&gt;可以设置安装时 root 用户的密码：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bash DebianNET.sh &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; 9 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 64 &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; WalterlvPwd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-29-21-14-23.png&quot; alt=&quot;自动安装&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你希望设置更多的安装选项，也可以选择手动安装。第三步我们修改命令。&lt;/p&gt;

&lt;p&gt;第三步：手动安装系统 Debian 9&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@walterlv ~]# bash DebianNET.sh &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; 9 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装过程和交互如下：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install&lt;/span&gt;

Do you want to &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;os manually?[y/n] y
Manual Mode insatll &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Debian] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;stretch] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;amd64] &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;VNC.

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Debian] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;stretch] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;amd64] Downloading...

It will reboot!
Please connect VNC!
Select Install OS &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;stretch amd64] to &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;system.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在以上命令重启 Linux 后，前往 VNC 界面选择启动的操作系统：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-29-20-57-28.png&quot; alt=&quot;InstallOS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后会进入安装和设置界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-29-20-57-46.png&quot; alt=&quot;安装界面&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 29 Jan 2020 13:58:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/collected-scripts-for-reinstalling-linux.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/collected-scripts-for-reinstalling-linux.html</guid>
        
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性？如何正确实现这个属性？</title>
        <description>&lt;p&gt;非泛型版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&lt;/code&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsSynchronized&lt;/code&gt; 属性和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyncRoot&lt;/code&gt; 属性，这两个属性被用来设计成以线程安全的方式访问和修改集合。不过这个设计让线程安全的访问由集合的实现方转嫁到了调用方，导致要么很难实现，要么很难调用。&lt;/p&gt;

&lt;p&gt;虽然泛型版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; 已经改进了设计，不再引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyncRoot&lt;/code&gt; 这样的属性到接口中，但如果我们在某些场景下需要实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&lt;/code&gt; 非泛型集合时，如何正确实现 SyncRoot 模式（SyncRoot Pattern）呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;先上结论：&lt;/p&gt;

&lt;p&gt;—— &lt;strong&gt;不可能正确实现 SyncRoot 模式&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在多线程程序设计中，为了在保证线程安全的同时避免死锁，&lt;strong&gt;不应该公开同步锁&lt;/strong&gt;。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&lt;/code&gt; 接口中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyncRoot&lt;/code&gt; 属性在接口中必然是公开的，于是没有任何途径可以保证调用方不会发生死锁。&lt;/p&gt;

&lt;p&gt;于是实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyncRoot&lt;/code&gt; 的正确方法应该是：&lt;/p&gt;

&lt;p&gt;—— &lt;strong&gt;避免公开 SyncRoot 属性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;所以 SyncRoot 模式应该这样实现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用显式接口实现，避免公开暴露此属性&lt;/li&gt;
  &lt;li&gt;抛出异常，避免调用者使用此属性&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;结合 .NET Core 源代码中的一些常用写法，我给出一个推荐的 SyncRoot 模式的写法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Is this List synchronized (thread-safe)?&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsSynchronized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Synchronization root for this object.&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyncRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;嗯，没错，返回了 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;，这是各种同步时绝对不应该使用的对象。然而这个属性都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt; 了，不管返回什么，与 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 还有什么区别……&lt;/p&gt;

&lt;p&gt;关于为什么同步时不应该返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 或者返回公开的对象，原因可以看我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/why-making-the-sync-root-public-is-dangerous&quot;&gt;为什么不应该公开用来同步的加锁对象？为什么不应该 lock(this)/lock(string) 或者 lock 任何非私有对象？ - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 28 Jan 2020 08:53:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/sync-root-on-collections.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/sync-root-on-collections.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>全民 https！使用 FreeSSL 申请免费的 https 证书</title>
        <description>&lt;p&gt;到现在还不为你的网站添加 https 的话，浏览器已经会非常显眼地显示“不安全”了。&lt;/p&gt;

&lt;p&gt;感谢 Let’s Encrypt，感谢 buypass，个人使用申请 https 证书的话已经可以免费了。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于域名&quot;&gt;关于域名&lt;/h2&gt;

&lt;p&gt;我们使用 FreeSSL.org 申请的是域名证书，对一个或多个域名生效。所以，你至少需要拥有一个域名。如果没有，去 &lt;a href=&quot;https://tld-list.com/&quot;&gt;https://tld-list.com/&lt;/a&gt; 输入你心仪的域名，然后找到最便宜的一家买一个吧！&lt;/p&gt;

&lt;h2 id=&quot;第一步输入域名&quot;&gt;第一步：输入域名&lt;/h2&gt;

&lt;p&gt;打开 &lt;a href=&quot;https://freessl.org/&quot;&gt;https://freessl.org/&lt;/a&gt;，在输入框中输入你想要申请证书的域名，然后点击“创建免费的SSL证书”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-19-55-56.png&quot; alt=&quot;FreeSSL.org&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面有提供商的选择，选 Let’s Encrypt V2 的话，我们可以申请泛域名证书，但有效期只有 3 个月。也就是说 3 个月之后你需要重新申请（重新申请的步骤可以简化，后面会说）。选择 buypass 的话，不能申请泛域名证书，但一次申请可以管 6 个月，比较省事儿。&lt;/p&gt;

&lt;p&gt;可能需要解释一下泛域名。泛域名是带通配符的域名，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;*.walterlv.com&lt;/code&gt; 就是一个泛域名。值得注意的是，这只能代表所有的二级域名。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;com&lt;/code&gt; 是一个顶级域名，&lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.com&lt;/code&gt; 是一个一级域名，&lt;blog.walterlv.com&gt; 是一个二级域名。而泛域名 `*.walterlv.com` 范围涵盖了二级域名 &lt;blog.walterlv.com&gt;，但是不包含一级域名 `walterlv.com` 和三级域名 `s.blog.walterlv.com`。&lt;/blog.walterlv.com&gt;&lt;/blog.walterlv.com&gt;&lt;/p&gt;

&lt;p&gt;所以你不能指望申请一个泛域名适用你的所有网站。但是！&lt;strong&gt;FreeSSL.org&lt;/strong&gt; 自动为你的泛域名创建两个证书，对我们初学者来说非常友好，不容易出错！如下图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-11-16.png&quot; alt=&quot;自动补全&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 当输入了一个泛域名之后，点击“创建免费的SSL证书”，会自动把上一级域名也自动生成了。&lt;/p&gt;

&lt;h2 id=&quot;第二步填写邮箱&quot;&gt;第二步：填写邮箱&lt;/h2&gt;

&lt;p&gt;输入你自己的邮箱，然后点击“点击创建”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-12-57.png&quot; alt=&quot;输入邮箱&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步安装并用-keymanager-打开&quot;&gt;第三步：安装并用 KeyManager 打开&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-16-35.png&quot; alt=&quot;提示用 KeyManager 打开&quot; /&gt;&lt;/p&gt;

&lt;p&gt;推荐下载安装 &lt;a href=&quot;https://keymanager.org/&quot;&gt;KeyManager&lt;/a&gt;，这可以在接下来的步骤当中省去一堆手工配置，也为将来重新申请证书带来更高的效率。&lt;/p&gt;

&lt;p&gt;下载安装完成后，如果打开 KeyManager 的提示已经消失，可以点击“再次尝试启动KeyManager”打开：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-20-06.png&quot; alt=&quot;再次尝试启动KeyManager&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开 KeyManager 后不需要任何操作，直接回到浏览器中刚刚的页面即可。（当然，如果提示登录或设置密码，则需要输入密码）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-21-28.png&quot; alt=&quot;打开 KeyManager 不需要任何操作&quot; /&gt;&lt;/p&gt;

&lt;p&gt;回到浏览器后点击“继续”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-22-41.png&quot; alt=&quot;继续&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第四步验证域名&quot;&gt;第四步：验证域名&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://freessl.org/&quot;&gt;FreeSSL.org&lt;/a&gt; 需要验证这个域名确实是你自己的，按照它的说明，去你的域名管理页面中配置一个或两个记录（取决于你申请几个证书）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-24-48.png&quot; alt=&quot;按照提示配置域名&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你需要前往你购买域名的域名提供商的网页里去设置。如果你已经改了域名服务器，就需要去改了之后的域名服务商那里设置。&lt;/p&gt;

&lt;p&gt;设置方法是添加一个新的设置，类型是 TXT，名称是和值是上面页面中给你提供的值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-29-23.png&quot; alt=&quot;设置 TXT 记录值&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第五步点击验证&quot;&gt;第五步：点击验证&lt;/h3&gt;

&lt;p&gt;回到 &lt;a href=&quot;https://freessl.org/&quot;&gt;FreeSSL.org&lt;/a&gt; 页面，点击“点击验证”，如果通过，这时会继续提示进入 KeyManager 软件。如果没有通过，不要紧，等几分钟再试，不同的域名服务器生效的时间有差异。&lt;/p&gt;

&lt;h3 id=&quot;第六步导出证书&quot;&gt;第六步：导出证书&lt;/h3&gt;

&lt;p&gt;在 KeyManager 的证书管理页面，点击单个域名最右边的“…”按钮，点击“详情”，拉倒最下面点击“查看证书”，再点击“导出证书”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-33-06.png&quot; alt=&quot;证书详情&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-33-54.png&quot; alt=&quot;查看证书&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-34-30.png&quot; alt=&quot;导出证书&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选择你希望导出的证书平台，决定是否要为证书设置密码，点击“导出”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-20-35-38.png&quot; alt=&quot;选择导出平台&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我应该选择哪个平台？&lt;/p&gt;

&lt;p&gt;如果你使用 Nginx 或 frp 反向代理服务器，那么导出为 Nginx 平台。参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-frp&quot;&gt;使用 Frp 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-nginx&quot;&gt;使用 Nginx 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你使用 IIS 反向代理服务器，或者直接使用 Kestrel 对外提供 https 粉刷说，那么导出为 IIS 平台。参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet-using-kestrel&quot;&gt;使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你使用 Apache 或者 Tomcat 作为 Web 服务器，则选择对应的平台。&lt;/p&gt;

&lt;h3 id=&quot;最后&quot;&gt;最后&lt;/h3&gt;

&lt;p&gt;将证书用于你的 Web 服务器，参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet&quot;&gt;三种方法为 ASP.NET Core 对外服务添加 https 支持（kestrel / frp / nginx）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 13 Jan 2020 00:05:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/apply-for-free-ssl-certificates-using-freessl.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/apply-for-free-ssl-certificates-using-freessl.html</guid>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>如何设置 ASP.NET Core 程序监听的 IP 和端口</title>
        <description>&lt;p&gt;Web 服务需要配置监听的 IP 和端口才可以对外提供真正的服务。本文介绍如何设置 ASP.NET Core 程序监听的 IP 和端口。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;ASP.NET Core 程序默认集成了 Kestrel 服务器，可以直接对外提供 Web 服务。虽然可以直接提供服务，但通常建议使用反向代理服务器来间接提供服务。因此，本文建议的大多数设置监听 IP 和端口的方法都是“临时方法”，即那种“配置出来”的方法，而不会直接写死在代码中。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何选择应该监听的-ip-和端口&quot;&gt;如何选择应该监听的 IP 和端口？&lt;/h2&gt;

&lt;p&gt;一般来说，监听的 IP 可以选择本地回环地址，特定的 IP 以及任意 IP，分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.2&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.3&lt;/code&gt;…… 本地回环地址&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;101.199.96.22&lt;/code&gt; 特定的 IP&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt; 任意 IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;监听本地回环地址时，则访问仅限于本机应用程序，不需要管理员权限来添加防火墙配置。如果在本地计算机配置了反向代理服务器，则强烈推荐使用本地回环地址。如果打算直接让服务对外公开提供，则需要设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt; 任意 IP。&lt;/p&gt;

&lt;p&gt;一台计算机上不同的应用不能使用相同的端口，对于端口的选择只要不重复即可。如果希望让 ASP.NET Core 程序自动选择一个不重复的端口，则将其指定为 0。&lt;/p&gt;

&lt;h2 id=&quot;配置方法&quot;&gt;配置方法&lt;/h2&gt;

&lt;h3 id=&quot;方法一直接在项目中设置&quot;&gt;方法一：直接在项目中设置&lt;/h3&gt;

&lt;p&gt;在项目上右击属性，在调试标签下可以修改应用的启动 URL。虽然这里修改的是项目的设置，最终生成的 ASP.NET Core 程序并不会使用这个设置，但每次通过项目打开时仍然可以使用这个设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-21-08-49.png&quot; alt=&quot;在项目中设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这种方式仅仅影响调试时候采用的域名 / IP 和端口号。因此，仅在调试期间生效，待发布后，可以直接接入到反向代理服务器中。&lt;/p&gt;

&lt;h3 id=&quot;方法二使用-kestrel-服务器的配置不推荐&quot;&gt;方法二：使用 Kestrel 服务器的配置（不推荐）&lt;/h3&gt;

&lt;p&gt;直接使用 Kestrel 服务器可以在没有反向代理服务器的情况下直接对外提供 ASP.NET Core 的 Web 服务。&lt;/p&gt;

&lt;p&gt;如果仅对外提供 http 服务，则设置非常简单：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =&amp;gt;
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =&amp;gt;
            {
&lt;span class=&quot;gi&quot;&gt;++              webBuilder.ConfigureKestrel(serverOptions =&amp;gt;
++              {
++                  serverOptions.Listen(&quot;0.0.0.0&quot;, 5000);
++              })
&lt;/span&gt;                .UseStartup&amp;lt;Startup&amp;gt;();
            });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果希望加上 https 的支持，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet-using-kestrel&quot;&gt;使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;方法三使用命令行参数指定&quot;&gt;方法三：使用命令行参数指定&lt;/h3&gt;

&lt;p&gt;使用命令行参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;--urls&lt;/code&gt; 可以为 ASP.NET Core 程序指定监听的 URL。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/blog.walterlv.com.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http://0.0.0.0:13800&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 URL 中的几个信息都会用到：http 协议，监听任意 IP 地址，监听端口 13800。&lt;/p&gt;

&lt;p&gt;通常建议为反向代理的服务选用 http，让 https 的支持交给反向代理服务器去做，参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet&quot;&gt;三种方法为 ASP.NET Core 对外服务添加 https 支持（kestrel / frp / nginx）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;方法四设置环境变量&quot;&gt;方法四：设置环境变量&lt;/h3&gt;

&lt;p&gt;设置环境变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;ASPNETCORE_URLS&lt;/code&gt; 即可为 ASP.NET Core 程序指定监听的 URL，格式与上面使用命令行参数是一样的。&lt;/p&gt;

&lt;p&gt;注意，这里说的环境变量是单独为某一个程序设置的环境变量，而不是为用户账户或者操作系统设置的环境变量（那样显然会让所有 ASP.NET Core 程序冲突）。所以通常都是用来在反向代理服务器中配置的。&lt;/p&gt;
</description>
        <pubDate>Mon, 13 Jan 2020 00:04:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/configure-urls-and-port-for-asp-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/configure-urls-and-port-for-asp-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>使用 Frp 为你的 Web 服务添加 https 支持</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;frp 是一个可用于内网穿透的高性能的反向代理应用，支持 tcp, udp 协议，为 http 和 https 应用协议提供了额外的能力，且尝试性支持了点对点穿透。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在众多反向代理应用中，frp 的最大特点就在于内网穿透。所以，如果你有将内网对外提供 Web 服务的需求，就可以考虑使用 frp 为你的 Web 服务提供 https 支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载-frp&quot;&gt;下载 frp&lt;/h2&gt;

&lt;p&gt;前往 GitHub 下载 frp：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/fatedier/frp/releases&quot;&gt;Releases · fatedier/frp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有适用于各种不同操作系统的 frp，如果你对外提供的公网服务器和实际提供 Web 服务的服务器不是同一台机器的话，需要为各自机器下载对应版本的 frp。&lt;/p&gt;

&lt;h2 id=&quot;准备好-web-服务和-ssl-证书&quot;&gt;准备好 Web 服务和 SSL 证书&lt;/h2&gt;

&lt;p&gt;你可以用任何方式开发你的 Web 服务，注意你的 Web 服务需要监听一个本机端口。&lt;/p&gt;

&lt;p&gt;对于准备 SSL 证书，你可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/apply-for-free-ssl-certificates-using-freessl&quot;&gt;使用 freessl.org 为你的域名申请免费的 SSL 证书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于本文的后续内容，你需要将证书导出成 Nginx 格式，即一个 crt 文件和一个 key 文件。&lt;/p&gt;

&lt;h2 id=&quot;配置-frp&quot;&gt;配置 frp&lt;/h2&gt;

&lt;p&gt;你需要准备运行一个 frp 服务端和一个 frp 客户端。它们可以运行在不同的机器上，也可以运行在同一台机器上。&lt;/p&gt;

&lt;p&gt;鉴于 frp 的内网穿透的优势，如果你将这两个端部署在不同的机器上，就能够实现 https 支持的同时也做到内网穿透——即你可以将 NAT 网络中的一台电脑对全球公开的互联网提供服务。&lt;/p&gt;

&lt;p&gt;当然，你也可以部署到同一台机器上，这样的优势就是一个端口可以服务很多的 Web 服务，同时支持 https。&lt;/p&gt;

&lt;p&gt;接下来的描述中，我用 A 机器表示 frp 服务端（也就是对公众开放服务的一端），B 机器表示 frp 客户端（提供 Web 服务的一端）。它们可以是同一台机器，也可以是不同的机器。&lt;/p&gt;

&lt;h3 id=&quot;反向代理服务端&quot;&gt;反向代理服务端&lt;/h3&gt;

&lt;p&gt;A 机器需要修改 frps.ini 文件：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[common]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;bind_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;7000&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;vhost_http_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;80&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;vhost_https_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;443&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ bind_port 是 frp 服务端口，客户端如果要使用 frp 服务则连接这个端口；vhost_http_port 是代理 http 的端口；vhost_https_port 是代理 https 的端口&lt;/p&gt;

&lt;p&gt;配置完成之后，运行 frp 程序：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/frps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/frps.ini&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 对于 Linux 系统&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/frps.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/frps.ini&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 对于 Windows 系统&lt;/p&gt;

&lt;p&gt;于是，A 机器就配置好了。&lt;/p&gt;

&lt;h3 id=&quot;反向代理客户端&quot;&gt;反向代理客户端&lt;/h3&gt;

&lt;p&gt;B 机器的配置将是 https 支持的重点：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[common]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 这里填写 A 机器的 IP 或者域名
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;server_addr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;100.13.*.*&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 填写 A 机器开放的 frp 服务端口，也就是 frps.ini 配置文件中 bind_port 的值
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;server_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;7000&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[walterlv_example_http]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 依然支持 http 访问
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 本地 Web 服务的端口
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;local_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10000&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 需要反向代理的域名（当访客通过此域名访问 A 机器时，才会将请求反向代理到此 Web 服务）
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;custom_domains&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example.walterlv.com&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[walterlv_example]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 配置 https 访问
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 本地 Web 服务的端口（与前面的配置一样，都对应同一个 Web 服务）
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;local_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10000&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 需要反向代理的域名（当访客通过此域名访问 A 机器时，才会将请求反向代理到此 Web 服务）
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;custom_domains&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example.walterlv.com&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 接下来的配置是支持 https 的重点配置
# 配置插件，将 https 请求转换成 http 请求后再发送给本地 Web 服务程序
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;plugin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https2http&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 转换成 http 后，发送到本机的 10000 端口
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;plugin_local_addr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;127.0.0.1:10000&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 可能是 frp 的 Bug？这里必须写成 127.0.0.1，稍后解释
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;plugin_host_header_rewrite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 指定代理方式为 frp
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;plugin_header_X-From-Where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;frp&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 指定成你在前面部分导出的证书的路径
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;plugin_crt_path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:/Samples/_.walterlv.com_chain.crt&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;plugin_key_path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;C:/Samples/_.walterlv.com_key.key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这就是 frp 的特色，重点配置都放到了反向代理的客户端中。这样的配置方式安全性自然成了问题，但也正因为如此，才可以真正实现带有内网穿透的反向代理。&lt;/p&gt;

&lt;p&gt;接下来介绍以下这个文件里面为什么是这样配置的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[Common]&lt;/code&gt; 节点是为了与 frp 服务端取得联系的。所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;server_addr&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;server_port&lt;/code&gt; 自然成了必要，毕竟连接一个 Web 服务这是两个必要的参数。如果你的两个端部署在同一台电脑上，那么这里可以填写 &lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[walterlv_example_http]&lt;/code&gt; 节点和 &lt;code class=&quot;highlighter-rouge&quot;&gt;[walterlv_example]&lt;/code&gt; 两个节点的名称是随便取的，不需要满足什么规律。唯一的要求是，连接到此 frp 服务端的所有客户端之间，这个名称都不能重复。frp 的服务端通过此名称来区分不同的客户端配置。因此，通常将这个名称命名成域名或者功能名。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[walterlv_example_http]&lt;/code&gt; 节点配置来兼容 http 访问。如果不配置这一个节点，那么使用 http 访问的访客将得到 frp 服务器返回的 403 状态码。这里的三项配置表示，如果使用 http 协议访问此 frp 服务端，且访问域名是 &lt;code class=&quot;highlighter-rouge&quot;&gt;example.walterlv.com&lt;/code&gt;（http 头里写的），那么将此请求转发到 frp 客户端本机的 &lt;code class=&quot;highlighter-rouge&quot;&gt;10000&lt;/code&gt; 端口。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[walterlv_example]&lt;/code&gt; 节点的前三项与 &lt;code class=&quot;highlighter-rouge&quot;&gt;[walterlv_example_http]&lt;/code&gt; 一样，含义也是一样的。接下来就是启用 &lt;code class=&quot;highlighter-rouge&quot;&gt;https2http&lt;/code&gt; 插件，将访问 frp 服务端的 https 流量全部转换成 http 流量，然后转发给本机的 http 服务。&lt;code class=&quot;highlighter-rouge&quot;&gt;plugin_local_addr&lt;/code&gt; 就是指定转发到本机的 &lt;code class=&quot;highlighter-rouge&quot;&gt;10000&lt;/code&gt; 端口。当然你也可以写成非本机的 http 服务，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.github.io:80&lt;/code&gt;，这样，https 流量转换成 http 流量后会发给对应的机器。&lt;code class=&quot;highlighter-rouge&quot;&gt;plugin_host_header_rewrite&lt;/code&gt; 在目前（frp 0.31.1 版本），这个值必须写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt;，否则会出现错误的重定向（例如，如果指定成 &lt;code class=&quot;highlighter-rouge&quot;&gt;example.walterlv.com&lt;/code&gt; 会导致流量回流到 frp 服务端，这绝对是反向代理的一个 Bug！）这个值的含义是修改 http 的请求头，将请求头中的域名部分改写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt;（在改写之前，头是 &lt;code class=&quot;highlighter-rouge&quot;&gt;example.walterlv.com&lt;/code&gt;）。&lt;code class=&quot;highlighter-rouge&quot;&gt;plugin_crt_path&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;plugin_key_path&lt;/code&gt; 指定为 SSL 证书的路径。&lt;code class=&quot;highlighter-rouge&quot;&gt;plugin_header_X-From-Where&lt;/code&gt; 则不是必须的。&lt;/p&gt;

&lt;h2 id=&quot;工作原理&quot;&gt;工作原理&lt;/h2&gt;

&lt;p&gt;使用 frp 让 Web 服务支持 https 的流程是一个典型的反向代理服务器的工作流程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-12-19-15-53.png&quot; alt=&quot;frp 反向代理支持 https 的流程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;访客在浏览器中输入网址 &lt;a href=&quot;https://blog.walterlv.com&quot;&gt;https://blog.walterlv.com&lt;/a&gt; 后，浏览器会查询 &lt;blog.walterlv.com&gt; 的 IP，查询到之后，向此 IP 的 443 端口发送 https 请求。frp 服务端收到此请求后检查访问的域名，发现曾经连接此 frp 服务端的一个客户端配置了此域名的反向代理。于是将请求转发给此客户端。frp 客户端在收到转发的 https 请求后，使用 SSL 证书将 https 解密成 http 请求，然后修改 http 头添加或修改额外的信息。最后，frp 客户端将修改后的 http 请求转发给本机的真正的 Web 服务程序。当 Web 服务程序处理完 Web 请求后，响应沿着原路返回。&lt;/blog.walterlv.com&gt;&lt;/p&gt;

&lt;p&gt;这里值得注意的是，由于 frp 反向代理系统中，使用 SSL 证书的一端在 frp 客户端，这意味着 frp 服务端完全无法得知此 https 请求的内容。于是在转发后也无法得知此请求的真实来源（访客 IP），这样，真实的 Web 服务将无法得知真实的访客信息。这也是 frp 在此设计下必然出现的缺陷。&lt;/p&gt;

&lt;p&gt;如果你希望你的 Web 服务在 https 下破除这些限制，那么建议使用其他的反向代理服务器。关于其他配置 https 的方法，你可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet&quot;&gt;三种方法为 ASP.NET Core 对外服务添加 https 支持（kestrel / frp / nginx）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet-using-kestrel&quot;&gt;使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-nginx&quot;&gt;使用 Nginx 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除了 frp 以外的方法都可以获得真实的访客信息。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/fatedier/frp/blob/master/README_zh.md&quot;&gt;frp/README_zh.md at master · fatedier/frp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 13 Jan 2020 00:03:28 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-https-support-for-web-service-using-frp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-https-support-for-web-service-using-frp.html</guid>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>为 ASP.NET Core 程序制作 URL 的 301/302 跳转</title>
        <description>&lt;p&gt;如果你有一些需要重定向网页 URL 的情况，可以返回 HTTP 状态码 301/302 告诉浏览器或者搜索引擎访问新的 URL。本文描述如何在 ASP.NET Core 中进行重定向。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;http-状态码-301302&quot;&gt;HTTP 状态码 301/302&lt;/h2&gt;

&lt;p&gt;301 表示“Moved Permanently”，即永久移动。通过返回此状态码可以告知浏览器或者搜索引擎此 URL 已经永久移动到了新的 URL 地址。搜索引擎会使用新的 URL 来更新自己的搜索结果，而浏览器会将此 URL 重定向缓存起来，下次访问的时候直接使用新的 URL 来访问。&lt;/p&gt;

&lt;p&gt;302 表示“Found”，发现；原始描述为“Moved Temporarily”，即临时移动。通过返回此状态码可以告知浏览器或者搜索引擎此 URL 临时移动到了新的 URL 地址。搜索引擎会使用此新的 URL 来抓取页面的内容但不会更新此 URL，而浏览器会访问新的 URL 但不会缓存此 URL 重定向。&lt;/p&gt;

&lt;p&gt;还有其他的重定向的 HTTP 状态码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;303 See Other&lt;/li&gt;
  &lt;li&gt;307 Temporary Redirect&lt;/li&gt;
  &lt;li&gt;308 Permanent Redirect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;301/302 本来设计为移动资源的时候保持方法不变，但各大浏览器在实现的时候对于 POST 方法，有的实现成了 GET 方法，有的实现成了 POST 方法。于是在后来的 HTTP 标准中将浏览器的错误实现变成了标准，301 和 302 方法要求使用 GET 方法重定向。不过由于历史原因无法保证一定是改用 GET 方法，所以增加了 303 状态码要求一定使用 GET 方法重定向。随后将原来本应该正确实现的 301 和 302 重新定义成 307 和 308 状态码，要求重定向时不允许修改方法。&lt;/p&gt;

&lt;h2 id=&quot;aspnet-core&quot;&gt;ASP.NET Core&lt;/h2&gt;

&lt;p&gt;ASP.NET Core 的 Blazor 框架生成的页面在路由的时候是不识别 &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; 后缀的，而带有 &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; 后缀的 URL 会被识别为静态文件。于是，如果创建了一个空的 Blazor 应用，当访问 &lt;a href=&quot;https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html&quot;&gt;https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html&lt;/a&gt; 网址的时候，会返回 404 Not Found，而不是路由到我的博客页面。&lt;/p&gt;

&lt;p&gt;如果我们将此 URL 重定向到不带后缀的 URL，则可以被 Blazor 框架识别并正确显示对应的博客页面。&lt;/p&gt;

&lt;p&gt;我们有两个不同的方式来实现这种 URL 的重定向：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;做一个重定向的控制器 &lt;code class=&quot;highlighter-rouge&quot;&gt;Controller&lt;/code&gt;，然后在控制器中重定向所有的博客页面&lt;/li&gt;
  &lt;li&gt;做一个重定向的中间件，对所有包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; 后缀的博客页面重定向到没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; 后缀的博客页面&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过，写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Controller&lt;/code&gt; 会要求这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Controller&lt;/code&gt; 路由到几乎所有的 URL 上，对其他功能很不利，所以中间件是最合适的方式。&lt;/p&gt;

&lt;h2 id=&quot;重定向中间件&quot;&gt;重定向中间件&lt;/h2&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
&lt;span class=&quot;gi&quot;&gt;++          app.UseAutoRemoveHtmlExtension();
&lt;/span&gt;            app.UseEndpoints(endpoints =&amp;gt;
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage(&quot;/_Host&quot;);
            });
            app.UseStaticFiles();
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Startup&lt;/code&gt; 类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configure&lt;/code&gt; 方法中可以添加中间件。为了实现去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; 后缀的中间件，我添加了一个自己的扩展方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseAutoRemoveHtmlExtension&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 自动移除所有的 .html 后缀，并永久重定向到没有 .html 后缀的网页。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;app&quot;&amp;gt;&amp;lt;see cref=&quot;IApplicationBuilder&quot;/&amp;gt;。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&amp;lt;see cref=&quot;IApplicationBuilder&quot;/&amp;gt;。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseAutoRemoveHtmlExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasValue&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.html&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 去掉 .html 后缀&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;301&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实现自己的中间件实际上直接调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;IApplicationBuilder&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Use&lt;/code&gt; 方法即可，传入一个委托用来在 URL 处理过程中添加一个步骤。&lt;/p&gt;

&lt;p&gt;两个参数，&lt;code class=&quot;highlighter-rouge&quot;&gt;context&lt;/code&gt; 中包含了本次请求的一些上下文，包括域名、URL 路径，返回的 HTTP 状态码。调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;context.Response.Redirect&lt;/code&gt; 方法可以进行 302 跳转。如果需要改成 301 跳转，则直接设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;context.Response.StatusCode&lt;/code&gt; 方法即可。&lt;/p&gt;

&lt;p&gt;接下来，对于不需要重定向的网址，我们直接交给后面的中间件处理，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await next()&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;重定向&quot;&gt;重定向&lt;/h2&gt;

&lt;p&gt;如果你希望做其他种类的跳转，你也可以添加新的中间件，比如：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将 HTTP 重定向到 HTTPS（谷歌建议使用 301 跳转）&lt;/li&gt;
  &lt;li&gt;你可以在打开某个网页之前要求登录，于是做一个 302 跳转到登录页面；&lt;/li&gt;
  &lt;li&gt;你可以将一些已经过时的网页进行 301 跳转到新的网页；
    &lt;ul&gt;
      &lt;li&gt;比如我将一些之前不太规范的博客 URL 重定向到统一的格式；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;你可以在迁移服务的时候临时做一个 302 跳转。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;小心缓存&quot;&gt;小心缓存&lt;/h2&gt;

&lt;p&gt;请注意，301 重定向会被浏览器缓存。也就是说如果你重定向到了一个错误的网址，那么再次访问的话浏览器将直接访问这个错误的网址。如果希望浏览器停止重定向到这个错误的网址，需要清除浏览器的缓存。所以使用 301 的时候需要谨慎一些。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/HTTP_302&quot;&gt;HTTP 302 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Jan 2020 14:08:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>web</category>
        
        <category>blazor</category>
        
      </item>
    
      <item>
        <title>使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持</title>
        <description>&lt;p&gt;Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器。它内置集成在了 ASP.NET Core 项目模板中，所以编写和对外开放一个 Web 服务会非常简单。&lt;/p&gt;

&lt;p&gt;虽然不推荐直接使用 Kestrel 对外提供 Web 服务，但为了简单的话，临时使用也是非常不错的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器。&lt;/p&gt;

&lt;p&gt;Kestrel 只是一个 Web 服务器，能够提供对外的 Web 服务；但它没有反向代理功能。也就是说当你使用 Kestrel 指定了一个端口后，这个端口的所有流量将被 Kestrel 处理，不能再与其他 Web 服务程序共用端口了。当然还有一些其他的原因（比如 Web 安全防护），所以通常并不推荐直接使用 Kestrel 对外提供 Web 服务。&lt;/p&gt;

&lt;p&gt;但有一点——Kestrel 内置集成在了 ASP.NET Core 项目模板中，所以编写和对外开放一个 Web 服务会非常简单，这也使得 Kestrel 值得被临时使用一下。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;配置-kestrel&quot;&gt;配置 Kestrel&lt;/h2&gt;

&lt;p&gt;当你使用 dotnet 命令或者 Visual Studio 创建 ASP.NET Core 项目后，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConfigureWebHostDefaults&lt;/code&gt; 扩展方法的委托参数中，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;webBuilder&lt;/code&gt; 对象可以用来配置 Kestrel 服务器。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =&amp;gt;
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =&amp;gt;
            {
&lt;span class=&quot;gi&quot;&gt;++              webBuilder.ConfigureKestrel(serverOptions =&amp;gt;
++              {
++                  // 在这里设置 Kestrel 的一些配置属性。
++              })
&lt;/span&gt;                .UseStartup&amp;lt;Startup&amp;gt;();
            });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;配置-https&quot;&gt;配置 https&lt;/h2&gt;

&lt;p&gt;配置 Kestrel 时，只需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;serverOptions&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Listen&lt;/code&gt; 方法设置监听的 IP 和端口。并且，可以额外写一个委托用来设置监听参数。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;listenOptions.UseHttps&lt;/code&gt; 即可使用 SSL 证书来支持 https 协议。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public static IHostBuilder CreateHostBuilder(string[] args) =&amp;gt;
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =&amp;gt;
            {
                webBuilder.ConfigureKestrel(serverOptions =&amp;gt;
                {
&lt;span class=&quot;gi&quot;&gt;++                  serverOptions.Listen(IPAddress.Any, 5000, listenOptions =&amp;gt;
++                  {
++                      listenOptions.UseHttps(
++                          @&quot;D:\blog.walterlv.com\ssl\blog-walterlv-com.pfx&quot;,
++                          &quot;Hqh#Q*QqV%@aCnx41UB%M31H&quot;);
++                  });
&lt;/span&gt;                })
                .UseStartup&amp;lt;Startup&amp;gt;();
            });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;这种把密码写在代码中的做法一定要拖出去打&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;不过我需要做博客中介绍以下这里是传入密码的，你可以采用其他的方式将密码存起来。比如放入 Windows 凭据管理器中，或者以其他加密的方式存在服务器/个人电脑上。&lt;/p&gt;

&lt;p&gt;如果不指定证书，也可以使用 https，不过这使用的是默认的配置，只能用在 &lt;code class=&quot;highlighter-rouge&quot;&gt;localhost&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;另外，如果你还没有 SSL 证书，可以先阅读我的另一篇博客了解如何申请免费的 SSL 证书：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/apply-for-free-ssl-certificates-using-freessl&quot;&gt;使用 freessl.org 为你的域名申请免费的 SSL 证书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;至此，你的 ASP.NET Core 服务已经可以通过 https 对外提供服务了。&lt;/p&gt;

&lt;h2 id=&quot;更多配置&quot;&gt;更多配置&lt;/h2&gt;

&lt;p&gt;除了在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Startup&lt;/code&gt; 中使用上文提供的配置代码之外，还可以为 https 配置其他参数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureKestrel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serverOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;serverOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureEndpointDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listenOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 配置终结点&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;serverOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureHttpsDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listenOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;listenOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SslProtocols&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SslProtocols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tls12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你的 Kestrel 服务面向多个域名，那么也可以配置不同的域名使用不同的证书配置：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureKestrel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serverOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;serverOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Listen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listenOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;listenOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHttps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpsOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X509Certificate2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;CertificateLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFromStoreCert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;CertificateLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFromStoreCert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;CertificateLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFromStoreCert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;httpsOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServerCertificateSelector&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用更强大的方法配置-https&quot;&gt;使用更强大的方法配置 https&lt;/h2&gt;

&lt;p&gt;其实我本不应该在博客后面贴上“更多配置”一章的，因为如果需要实现更强大的功能，配置带有反向代理功能的 Web 服务器会强大得多。&lt;/p&gt;

&lt;p&gt;可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet&quot;&gt;三种方法为 ASP.NET Core 对外服务添加 https 支持（kestrel / frp / nginx）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-frp&quot;&gt;使用 Frp 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-nginx&quot;&gt;使用 Nginx 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1&quot;&gt;ASP.NET Core 中的 Kestrel Web 服务器实现 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 Jan 2020 12:10:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-https-support-for-asp-dotnet-using-kestrel.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-https-support-for-asp-dotnet-using-kestrel.html</guid>
        
        
        <category>dotnet</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>三种方法为 ASP.NET Core 对外服务添加 https 支持（kestrel / frp / nginx）</title>
        <description>&lt;p&gt;虽然使用 Visual Studio 创建 ASP.NET Core 程序的时候可以选择是否添加 https 支持，不过这种方式只添加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;localhost&lt;/code&gt; 的证书，只有本地访问时浏览器才会承认。真正对外公开服务的时候这样是绝对没法儿提供 https 服务的。&lt;/p&gt;

&lt;p&gt;本文介绍使用三种不同的方式添加 https 的支持，三种方法各有优劣，本文会进行比较并给出不同的适用场景。你自己选择就好。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;你需要有一个证书&quot;&gt;你需要有一个证书&lt;/h2&gt;

&lt;p&gt;如果你还没有证书，可以考虑去 &lt;a href=&quot;https://freessl.org/&quot;&gt;https://freessl.org/&lt;/a&gt; 免费申请一个。可以为泛域名申请 3 个月有效期的证书（Let’s Encrypt），或者为单域名申请 6 个月有效期的证书（buypass）。&lt;/p&gt;

&lt;p&gt;如果不知道如何操作，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/apply-for-free-ssl-certificates-using-freessl&quot;&gt;使用 freessl.org 为你的域名申请免费的 SSL 证书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你可以在以上博客中得到四种不同格式的证书（Nginx/Apache/IIS/Tomcat），下面的方法中每一种方法会使用到其中的一种证书。&lt;/p&gt;

&lt;h2 id=&quot;方法&quot;&gt;方法&lt;/h2&gt;

&lt;p&gt;实际上，只要是一个 Web 服务器就可以为 ASP.NET Core 服务程序提供 https 的支持，不过本文只会介绍下面这三种方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Kestrel
    &lt;ul&gt;
      &lt;li&gt;这是 ASP.NET Core 自带提供的 Web 服务器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Frp
    &lt;ul&gt;
      &lt;li&gt;这是一个开源即将收费的反向代理服务&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Nginx
    &lt;ul&gt;
      &lt;li&gt;这是非常强大的 Web 服务器，同时也是强大的反向代理服务器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kestrel 最简单，几句代码即可配完。Frp 相对来说也很简单。而 Nginx 非常强大，几乎适用于各种 Web 服务场景。&lt;/p&gt;

&lt;p&gt;Nginx 支持 http2，Kestrel 的 Windows 和 Linux 版本支持 http2。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;发现写成一篇博客会模糊这些方法之间的步骤，所以我将它们分别写成了几篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-asp-dotnet-using-kestrel&quot;&gt;使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-frp&quot;&gt;使用 Frp 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-https-support-for-web-service-using-nginx&quot;&gt;使用 Nginx 为你的 Web 服务添加 https 支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 Jan 2020 12:10:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-https-support-for-asp-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-https-support-for-asp-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>.NET 将多个程序集合并成单一程序集的 4+3 种方法</title>
        <description>&lt;p&gt;编写 .NET 程序的时候，我们经常会在项目的输出目录下发现一大堆的文件。除了我们项目自己生成的程序集之外，还能找到这个项目所依赖的一大堆依赖程序集。有没有什么方法可以把这些依赖和我们的程序集合并到一起呢？&lt;/p&gt;

&lt;p&gt;本文介绍四种将程序集和依赖打包合并到一起的方法，每一种方法都有其不同的原理和优缺点。我将介绍这些方法的原理并帮助你决定哪种方法最适合你想要使用的场景。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;四种方法&quot;&gt;四种方法&lt;/h2&gt;

&lt;p&gt;目前我已知的将 .NET 程序集与依赖合并到一起的方法有下面四种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖&lt;/li&gt;
  &lt;li&gt;使用 Fody&lt;/li&gt;
  &lt;li&gt;使用 SourceYard 源代码包&lt;/li&gt;
  &lt;li&gt;使用 ILMerge（微软所写）或者 ILRepack（基于 Mono.Ceil）&lt;/li&gt;
  &lt;li&gt;其他方法&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你还知道有其他的方法，欢迎评论指出，非常感谢！&lt;/p&gt;

&lt;p&gt;上面的第五种方法我也会做一些介绍，要么是因为无法真正完成任务或者适用场景非常有限，要么是其原理我还不理解，因此只进行简单介绍。&lt;/p&gt;

&lt;h3 id=&quot;使用-net-core-30-自带的-publishsinglefile-属性合并依赖&quot;&gt;使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖&lt;/h3&gt;

&lt;p&gt;.NET Core 3.0 自 Preview 5 开始，增加了发布成单一 exe 文件的功能。&lt;/p&gt;

&lt;p&gt;在你的项目文件中增加下面的两行可以开启此功能：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
    
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;RuntimeIdentifier&amp;gt;win10-x64&amp;lt;/RuntimeIdentifier&amp;gt;
++      &amp;lt;PublishSingleFile&amp;gt;true&amp;lt;/PublishSingleFile&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;
    
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一行 &lt;code class=&quot;highlighter-rouge&quot;&gt;RuntimeIdentifier&lt;/code&gt; 一定需要指定，因为发布的单一文件是特定于架构的。这里，我们指定了 win10-x64，你也可以指定为其他的值。可以使用的值你可以在这篇文章中查询到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/rid-catalog&quot;&gt;.NET Core Runtime IDentifier (RID) catalog - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第二行 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishSingleFile&lt;/code&gt; 即开启发布时单一文件的功能。这样，你在发布你的程序的时候可以得到一个单一的可执行程序。发布一个 .NET Core 项目的方法是在命令行中输入：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，如果你没有更改任何你的项目文件（没有增加上面的那两行），那么你在使用发布命令的时候就需要把这两个属性再增加上。因此完整的发布命令是下面这样的：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;win10-x64&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/p:PublishSingleFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;-r&lt;/code&gt; 就等同于在项目中指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;RuntimeIdentifier&lt;/code&gt; 持续。这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;/p&lt;/code&gt; 是在项目中增加一个属性，而增加的属性名是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishSingleFile&lt;/code&gt;，增加的属性值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;使用 .NET Core 3.0 这种自带的发布单一 exe 的方法会将你的程序的全部文件（包括所有依赖文件，包括非托管程序集，包括各种资源文件）全部打包到一个 exe 中。当运行这个 exe 的时候，会首先将所有这些文件生成到本地计算机中一个临时目录下。只有第一次运行这个 exe 的时候才会生成这个目录和其中的文件，之后的运行是不会再次生成的。&lt;/p&gt;

&lt;p&gt;下面说一些 .NET Core 3.0 发布程序集的一点扩展——.NET Core 3.0 中对于发布程序集的三种处理方式可以放在一起使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;裁剪程序集（Assembly Trimmer）&lt;/li&gt;
  &lt;li&gt;提前编译（Ahead-of-Time compilation，通过 crossgen）&lt;em&gt;后面马上会说到 Microsoft.DotNet.ILCompiler&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;单一文件打包（Single File Bundling）&lt;em&gt;本小节&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 .NET Core 3.0 中发布仅一个 exe 的方法、原理和实践，可以参见林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-%E5%8F%91%E5%B8%83%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA-exe-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet core 发布只有一个 exe 的方法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;.NET Core 在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet&quot;&gt;.NET Foundation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;使用-fody&quot;&gt;使用 Fody&lt;/h3&gt;

&lt;p&gt;在你的项目中安装一个 NuGet 包 &lt;a href=&quot;https://www.nuget.org/packages/Costura.Fody/&quot;&gt;Costura.Fody&lt;/a&gt;。一般来说，安装完之后，你编译的时候就会生成仅有一个 exe 的程序集了。&lt;/p&gt;

&lt;p&gt;如果你继续留意，可以发现项目中多了一个 Fody 的专属配置文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;FodyWeavers.xml&lt;/code&gt;，内容如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Weavers&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Costura/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Weavers&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;仅仅到此为止你已经足够利用 Fody 完成程序集的合并了。&lt;/p&gt;

&lt;p&gt;但是，如果希望对 Fody 进行更精细化的配置，可以阅读叶洪的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/iron_ye/article/details/83961266&quot;&gt;.NET 合并程序集（将 dll 合并到 exe 中） - Iron 的博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fody 在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Fody/Fody&quot;&gt;Fody/Fody: Extensible tool for weaving .net assemblies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;使用-sourceyard-源代码包&quot;&gt;使用 SourceYard 源代码包&lt;/h3&gt;

&lt;p&gt;SourceYard 源代码包在程序集合并上是另辟蹊径的一种合并方式。它不能帮助你将所有的依赖全部合并，但足以让你在发布一些简单应用的时候不至于引入大量的依赖。&lt;/p&gt;

&lt;p&gt;例如，你可以考虑新建一个项目，然后安装下面的 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/lindexi.src.MacAddress.Source/&quot;&gt;lindexi.src.MacAddress.Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安装完成之后，你就可以在你的项目中使用到此 NuGet 包为你带来的获取 MAC 地址的工具类了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;lindexi.src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;macList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MacAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetActiveMacAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mac&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;macList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译完你的项目，你会发现你的项目没有携带任何依赖。你安装的 NuGet 包并没有成为你的依赖，反而成为你正在编译的程序集的一部分。&lt;/p&gt;

&lt;p&gt;如果你要制作一个像上面那样的源代码包，只需要在你要制作 NuGet 包的项目安装上 &lt;a href=&quot;https://www.nuget.org/packages/dotnetCampus.SourceYard/0.1.7213-alpha&quot;&gt;dotnetCampus.SourceYard&lt;/a&gt;，在你打包成 NuGet 包的时候，就会生成一个普通的 NuGet 包以及一个 *.Source.nupkg 的源代码包。将源代码包上传到 nuget.org 上，其他人便可以安装你制作的源代码包了。&lt;/p&gt;

&lt;p&gt;关于如何使用 SourceYard 制作一个源代码包的方法可以阅读林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/sourceyard-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;SourceYard 制作源代码包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于能够做出源代码包的原理，可以阅读我的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;入门篇：&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;进阶篇：&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SourceYard 在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard: Add a NuGet package only for dll reference? By using dotnetCampus.SourceYard, you can pack a NuGet package with source code. By installing the new source code package, all source codes behaviors just like it is in your project.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;使用-ilmerge-或者-ilrepack-等工具&quot;&gt;使用 ILMerge 或者 ILRepack 等工具&lt;/h3&gt;

&lt;p&gt;ILMerge 和 ILRepack 的合并就更加富有技术含量——当然坑也更多。&lt;/p&gt;

&lt;p&gt;这两个都是工具，因此，你需要将工具下载下来使用。你有很多种方法下载到工具使用，因此我会推荐不同的人群使用不同的工具。&lt;/p&gt;

&lt;h4 id=&quot;ilmerge&quot;&gt;ILMerge&lt;/h4&gt;

&lt;p&gt;ILMerge 命令行工具是微软官方出品，下载地址：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=17630&quot;&gt;Download ILMerge from Official Microsoft Download Center&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其使用方法请参见我的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/merge-assemblies-using-ilmerge&quot;&gt;.NET 使用 ILMerge 合并多个程序集，避免引入额外的依赖 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;ilrepack&quot;&gt;ILRepack&lt;/h4&gt;

&lt;p&gt;ILRepack 基于 Mono.Ceil 来进行 IL 合并，其使用方法可以参见我的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/merge-assemblies-using-ilrepack&quot;&gt;.NET 使用 ILRepack 合并多个程序集（替代 ILMerge），避免引入额外的依赖 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;ilmerge-gui-工具已过时但适合新手随便玩玩&quot;&gt;ILMerge-GUI 工具（已过时，但适合新手随便玩玩）&lt;/h4&gt;

&lt;p&gt;你可以在以下网址中找到 ILMerge-GUI 的下载链接：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://bitbucket.org/wvd-vegt/ilmergegui/downloads/?tab=downloads&quot;&gt;wvd-vegt / ilmergegui / Downloads — Bitbucket&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ILMerge-GUI 工具在 Bitbucket 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://bitbucket.org/wvd-vegt/ilmergegui/src/master/&quot;&gt;wvd-vegt / ilmergegui — Bitbucket&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;其他方法&quot;&gt;其他方法&lt;/h3&gt;

&lt;h4 id=&quot;使用-microsoftdotnetilcompiler&quot;&gt;使用 Microsoft.DotNet.ILCompiler&lt;/h4&gt;

&lt;p&gt;可以将 .NET Core 编译为单个无依赖的 Native 程序。&lt;/p&gt;

&lt;p&gt;你需要先安装一个预览版的 NuGet 包 &lt;a href=&quot;https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.DotNet.ILCompiler&quot;&gt;Microsoft.DotNet.ILCompiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于 Microsoft.DotNet.ILCompiler 的使用，你可以阅读林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-%E4%BD%BF%E7%94%A8-CoreRT-%E5%B0%86%E7%A8%8B%E5%BA%8F%E7%BC%96%E8%AF%91%E4%B8%BA-Native-%E7%A8%8B%E5%BA%8F.html&quot;&gt;dotnet core 使用 CoreRT 将程序编译为 Native 程序&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;使用-dnspy&quot;&gt;使用 dnSpy&lt;/h4&gt;

&lt;p&gt;dnSpy 支持添加一个模块到程序集，也可以创建模块，还可以将程序集转换为模块。因此，一个程序集可以包含多个模块的功能就可以被充分利用起来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-30-11-44-16.png&quot; alt=&quot;添加模块到程序集&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;使用-warp&quot;&gt;使用 Warp&lt;/h4&gt;

&lt;p&gt;Warp 在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dgiagio/warp&quot;&gt;dgiagio/warp: Create self-contained single binary applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其使用可以参见林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-%E5%8F%91%E5%B8%83%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA-exe-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet core 发布只有一个 exe 的方法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;各种方法的原理和使用场景比较&quot;&gt;各种方法的原理和使用场景比较&lt;/h2&gt;

&lt;h3 id=&quot;原理&quot;&gt;原理&lt;/h3&gt;

&lt;p&gt;使用 .NET Core 3.0 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishSingleFile&lt;/code&gt; 属性合并依赖，其原理是生成一个启动器容器程序。最终没有对程序进行任何修改，只是单纯的打包而已。&lt;/p&gt;

&lt;p&gt;使用 Fody，是将程序集依赖放到了资源里面。当要加载程序集的时候，会直接将资源中的程序集流加载到内存中。&lt;/p&gt;

&lt;p&gt;使用 SourceYard 源代码包，是直接将源代码合并到了目标项目里面。&lt;/p&gt;

&lt;p&gt;使用 ILMerge / ILRepack，是在 IL 级别对程序集进行了合并。&lt;/p&gt;

&lt;p&gt;我们可以通过下面一张图来感受一下后三种原理上的不同。&lt;/p&gt;

&lt;p&gt;这是一个分别通过 Fody、SourceYard 和 ILMerge / ILRepack 生成的程序集的反编译图。可以看到，对于 ILRepack / ILMerge 和 SourceYard，反编译后看到的源代码都在目标程序集中，而对于 Fody，依赖仅仅出现在资源中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-30-12-35-31.png&quot; alt=&quot;原理差别&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;适用范围&quot;&gt;适用范围&lt;/h3&gt;

&lt;p&gt;由于其原理不同，所以其适用范围和造成的副作用也不同。&lt;/p&gt;

&lt;p&gt;如果你基于 .NET Core 3.0 开发，并且也不在意在目标计算机上生成的临时文件夹，那么可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishSingleFile&lt;/code&gt; 属性合并依赖。&lt;/p&gt;

&lt;p&gt;如果你不在乎启动性能以及内存消耗，那么可以考虑 Fody（这意味着小型程序比较适合采用）。&lt;/p&gt;

&lt;p&gt;如果你的程序非常在乎启动性能，那么就需要考虑 SourceYard、ILMerge / ILRepack 了。&lt;/p&gt;

&lt;p&gt;对于 ILMerge / ILRepack 和 SourceYard 的比较，可以看下面这张表格：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;方案&lt;/th&gt;
      &lt;th&gt;ILRepack / ILMerge&lt;/th&gt;
      &lt;th&gt;SourceYard&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;适用于&lt;/td&gt;
      &lt;td&gt;任意 .NET 程序集&lt;/td&gt;
      &lt;td&gt;通过 SourceYard 发布的 NuGet 包&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;WPF&lt;/td&gt;
      &lt;td&gt;ILRepack 支持，ILMerge 不支持&lt;/td&gt;
      &lt;td&gt;支持&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;调试（支持）&lt;/td&gt;
      &lt;td&gt;仅支持一般方法的调试&lt;/td&gt;
      &lt;td&gt;支持一般程序集支持的所有调试方法&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;调试（不支持）&lt;/td&gt;
      &lt;td&gt;不支持异步方法调试，不支持显示局部变量&lt;/td&gt;
      &lt;td&gt;没有不支持的&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;隐藏 API&lt;/td&gt;
      &lt;td&gt;internal 的类型和成员可以隐藏&lt;/td&gt;
      &lt;td&gt;必须是 private 类型和成员才可隐藏&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;可以发现，如果我们能够充分将我们需要的包通过 SourceYard 发布成 NuGet，那么我们将可以获得比 ILRepack / ILMerge 更好的编写和调试体验。&lt;/p&gt;

&lt;p&gt;表格之外还有一些特别需要说明的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;ILRepack 额外支持修改 WPF 编译生成的 Baml 文件，将资源的引用路径修改成新程序集的路径。&lt;/li&gt;
  &lt;li&gt;SourceYard 的类型需要写成 private 才可以隐藏，但是只有内部类才可以写 private，因此如果特别需要隐藏，请首先写一个内部类。（因此，你可能会发现有一个类型有很多个分部类，每一个分部类中都是一个私有的内部类）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;开源社区&quot;&gt;开源社区&lt;/h2&gt;

&lt;p&gt;最后说一下，以上所说的所有方法全部是开源的，有问题欢迎在社区讨论一起解决：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet&quot;&gt;.NET Foundation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Fody/Fody&quot;&gt;Fody/Fody&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet-campus/SourceYard&quot;&gt;dotnet-campus/SourceYard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge&quot;&gt;dotnet/ILMerge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack&quot;&gt;gluck/il-repack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/0xd4d/dnSpy&quot;&gt;0xd4d/dnSpy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dgiagio/warp&quot;&gt;dgiagio/warp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/corert/tree/master/pkg/Microsoft.DotNet.ILCompiler&quot;&gt;corert/pkg/Microsoft.DotNet.ILCompiler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 10 Jan 2020 13:52:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-merge-dotnet-assemblies.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-merge-dotnet-assemblies.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 中选择合适的文件打开模式（CreateNew, Create, Open, OpenOrCreate, Truncate, Append）</title>
        <description>&lt;p&gt;.NET 中文件打开的 API &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Open&lt;/code&gt; 提供了多种不同的文件打开方式，这些方式大多数与 Windows 文件 API 中的模式是对应的，但也有一些 .NET 层面的判断以及名称的变化。在 .NET 层你可以选择适合你业务场景需要的文件打开方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;
&lt;h2 id=&quot;文件打开方式&quot;&gt;文件打开方式&lt;/h2&gt;

&lt;p&gt;文件打开的多个重载方法中，除了封装好的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OpenRead&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;OpenWrite&lt;/code&gt; 之外，其他都是需要指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileMode&lt;/code&gt; 参数的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileStream&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;filemode&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FileMode&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;FileMode 枚举有 6 种不同的值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CreateNew&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;OpenOrCreate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Truncate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了方便查阅，我先将大家可能关心的内容做一个表格：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;FileMode&lt;/th&gt;
      &lt;th&gt;如果文件存在&lt;/th&gt;
      &lt;th&gt;如果文件不存在&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CreateNew&lt;/td&gt;
      &lt;td&gt;IOException&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Create&lt;/td&gt;
      &lt;td&gt;截断&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Open&lt;/td&gt;
      &lt;td&gt;打开&lt;/td&gt;
      &lt;td&gt;FileNotFoundException&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;OpenOrCreate&lt;/td&gt;
      &lt;td&gt;打开&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Truncate&lt;/td&gt;
      &lt;td&gt;截断&lt;/td&gt;
      &lt;td&gt;FileNotFoundException&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Append&lt;/td&gt;
      &lt;td&gt;追加&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;所有这些打开模式都不会修改到文件的属性（Attribute），包括创建时间、针对用户的权限设置。所以如果你希望连这些属性都不需要，而是完完全全创建新的文件，那么请先将原来的文件删除。&lt;/p&gt;

&lt;p&gt;注意，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Open&lt;/code&gt; 方法中传入以下这些参数的含义描述中可能有一些包含过程和判断的语句，但实际上这些真正的判断和过程发生在 Windows 内核（虽然 .NET 也有一些判断，但是一些参数预判断和参数转换），所以实际拿到文件流（对应 Win32 中拿到句柄）是一个原子操作，不会因为中间加了判断导致与其他线程发生竞争。&lt;/p&gt;

&lt;h3 id=&quot;createnew&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNew&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件不存在，则创建一个新的文件并返回新文件的文件流。如果文件已经存在，则抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;create&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Create&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件不存在，则创建一个新的文件并返回新文件的文件流。如果文件已经存在，则打开文件并返回此文件的文件流。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改会完全复写文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;open&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Open&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，则打开文件并返回此文件的文件流。如果文件不存在，则抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileNotFoundException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改不会截断文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111terlv&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;openorcreate&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OpenOrCreate&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，则打开文件并返回此文件的文件流。如果文件不存在，则创建一个文件并返回新文件的文件流。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改不会截断文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111terlv&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;truncate&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Truncate&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，则打开后文件的长度直接变为 0，随后返回此文件的文件流。如果文件不存在，则会抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileNotFoundException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;由于在打开文件时就已经将文件设置为 0 字节，所以对应到上面截断的描述是一定会截断的。写入任何新内容到文件候，文件中都不会存在旧文件中的内容。&lt;/p&gt;

&lt;h3 id=&quot;append&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Append&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件不存在，则创建一个新的文件并返回新文件的文件流。如果文件已经存在，则创建一个可以往文件的结尾处开始写的文件流。&lt;/p&gt;

&lt;p&gt;如果试图从文件流中往前倒推找到此前的文件内容，会抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;配合文件打开权限&quot;&gt;配合文件打开权限&lt;/h2&gt;

&lt;p&gt;在以上这些 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileMode&lt;/code&gt; 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNew&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Create&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Truncate&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Append&lt;/code&gt; 都是需要写文件的权限的，&lt;code class=&quot;highlighter-rouge&quot;&gt;OpenOrCreate&lt;/code&gt; 是否需要写权限则取决于文件是否存在。&lt;/p&gt;

&lt;h2 id=&quot;附源码&quot;&gt;附源码&lt;/h2&gt;

&lt;p&gt;以下是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileStream&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Open&lt;/code&gt; 方法最终调用处。可以发现，此方法将传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileMode&lt;/code&gt; 转换成了 Win32 中的值，并且最终调用了 Windows API &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateFile&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你可以阅读我的另一篇博客了解 Win32 API 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateFile&lt;/code&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win32-file-open-modes&quot;&gt;Win32 方法 CreateFile 中选择合适的文件打开模式（CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SafeFileHandle&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateFileOpenHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileShare&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;share&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SECURITY_ATTRIBUTES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secAttrs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSecAttrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;share&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fAccess&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_access&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERIC_READ&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_access&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERIC_WRITE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Our Inheritable bit was stolen from Windows, but should be set in&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// the security attributes class.  Don't leave this bit set.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;share&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileShare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inheritable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Must use a valid Win32 constant here...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenOrCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flagsAndAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// For mitigating local elevation of privilege attack through named pipes&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// make sure we always call CreateFile with SECURITY_ANONYMOUS so that the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// named pipe server can't impersonate a high privileged client security context&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (note that this is the effective default on CreateFile2)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flagsAndAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SECURITY_SQOS_PRESENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SECURITY_ANONYMOUS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisableMediaInsertionPrompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValidateFileHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;share&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secAttrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flagsAndAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Contains constants for specifying how the OS should open a file.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// These will control whether you overwrite a file, open an existing&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// file, or some combination thereof.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// To append to a file, use Append (which maps to OpenOrCreate then we seek&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// to the end of the file).  To truncate a file or create it if it doesn't&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// exist, use Create.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Creates a new file. An exception is raised if the file already exists.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CreateNew&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Creates a new file. If the file already exists, it is overwritten.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Opens an existing file. An exception is raised if the file does not exist.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Opens the file if it exists. Otherwise, creates a new file.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;OpenOrCreate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Opens an existing file. Once opened, the file is truncated so that its&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// size is zero bytes. The calling process must open the file with at least&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// WRITE access. An exception is raised if the file does not exist.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Truncate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Opens the file if it exists and seeks to the end.  Otherwise,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// creates a new file.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.io.filemode?view=netframework-4.8&quot;&gt;FileMode Enum (System.IO) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 08 Jan 2020 07:05:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-file-open-modes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-file-open-modes.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Win32 方法 CreateFile 中选择合适的文件打开模式（CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING）</title>
        <description>&lt;p&gt;Windows 打开文件的 API 中提供了多种不同的文件打开方式。你可以根据你的业务场景选择适合你的文件打开方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;
&lt;h2 id=&quot;windows-api&quot;&gt;Windows API&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OpenFile&lt;/code&gt; 方法只能打开已经存在的文件，而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateFile&lt;/code&gt; 则可以在打开文件的同时应对不存在文件时的创建。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;HANDLE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateFileW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPCWSTR&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;lpFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DWORD&lt;/span&gt;                 &lt;span class=&quot;n&quot;&gt;dwDesiredAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DWORD&lt;/span&gt;                 &lt;span class=&quot;n&quot;&gt;dwShareMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPSECURITY_ATTRIBUTES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpSecurityAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DWORD&lt;/span&gt;                 &lt;span class=&quot;n&quot;&gt;dwCreationDisposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DWORD&lt;/span&gt;                 &lt;span class=&quot;n&quot;&gt;dwFlagsAndAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;HANDLE&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;hTemplateFile&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;dwCreationDisposition&lt;/code&gt; 参数用来指定文件打开的时候如何处理文件的创建和追加行为。&lt;/p&gt;

&lt;h2 id=&quot;dwcreationdisposition&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dwCreationDisposition&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dwCreationDisposition&lt;/code&gt; 可以传入 5 种不同的值。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_NEW&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_ALWAYS&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_EXISTING&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_ALWAYS&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TRUNCATE_EXISTING&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了方便查阅，我先将大家可能关心的内容做一个表格：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dwCreationDisposition&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;如果文件存在&lt;/th&gt;
      &lt;th&gt;如果文件不存在&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_NEW&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_EXISTS&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_ALWAYS&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;截断&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_EXISTING&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;打开&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_NOT_FOUND&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_ALWAYS&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;打开&lt;/td&gt;
      &lt;td&gt;新建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TRUNCATE_EXISTING&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;截断&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_NOT_FOUND&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;所有这些打开模式都不会修改到文件的属性（Attribute），包括创建时间、针对用户的权限设置。所以如果你希望连这些属性都不需要，而是完完全全创建新的文件，那么请先将原来的文件删除。&lt;/p&gt;

&lt;h3 id=&quot;create_new&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_NEW&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件不存在，则创建一个文件。如果文件不存在，则执行失败，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetLastError&lt;/code&gt; 可以得到错误码 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_EXISTS&lt;/code&gt; (80)。&lt;/p&gt;

&lt;h3 id=&quot;create_always&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_ALWAYS&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件不存在，则创建一个新的文件。如果文件已经存在，则此文件将完全被复写。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改会完全复写文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;open_existing&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_EXISTING&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，则打开文件。如果文件不存在，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetLastError&lt;/code&gt; 可以得到错误码 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_NOT_FOUND&lt;/code&gt; (2)。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改不会截断文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111terlv&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;open_always&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OPEN_ALWAYS&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，那么会成功打开文件；并且也可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetLastError&lt;/code&gt; 可以得到状态 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_ALREADY_EXISTS&lt;/code&gt; (183)。如果文件不存在，新建一个文件。&lt;/p&gt;

&lt;p&gt;基于此文件流的修改不会截断文件。也就是说，如果原文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，通过此文件流写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;111&lt;/code&gt;，那么最终文件内容是 &lt;code class=&quot;highlighter-rouge&quot;&gt;111terlv&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;truncate_existing&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TRUNCATE_EXISTING&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果文件存在，则打开后文件的长度直接变为 0。如果文件不存在，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetLastError&lt;/code&gt; 可以得到错误码 &lt;code class=&quot;highlighter-rouge&quot;&gt;ERROR_FILE_NOT_FOUND&lt;/code&gt; (2)。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew&quot;&gt;CreateFileW function (fileapi.h) - Win32 apps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 08 Jan 2020 07:05:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win32-file-open-modes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win32-file-open-modes.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 中如何创建忽略 DPI 属性的图片</title>
        <description>&lt;p&gt;WPF 框架设计为与 DPI 无关，但你依然可能遇到 DPI 问题。尤其是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt; 控件显示的图片会根据图片 EXIF 中的 DPI 信息和屏幕 DPI 自动缩放图片。对于 UI 用图来说这是好事，但对于软件用户随便插入的图片来说就不是了——用户传入的图片可能是各种各样不统一的 DPI。因此这种 DPI 我们应该忽略。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;p&gt;直接设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt; 控件的大小是一个不错的方案，这在允许设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt; 控件大小的场合下是可以使用的。如果你能设置，那么直接设置，这是最好的方法了。&lt;/p&gt;

&lt;p&gt;除此之外，我们还可能可以尝试这些方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitmapImage&lt;/code&gt; 对象，根据当前屏幕的 DPI 值计算 &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelWidth&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelHeight&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingImage&lt;/code&gt; 对象，直接按照 WPF 的坐标单位绘制图片原始像素大小的图片；&lt;/li&gt;
  &lt;li&gt;创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bitmap&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 对象，重新创建一张 96 DPI 的图片。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以下的代码中，都假设当前 DPI 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;monitorDpi&lt;/code&gt;。&lt;/p&gt;

&lt;!-- ## `BitmapImage`

如果直接使用 `BitmapImage` 对象，那么需要事先得知图片的宽高，否则需要两次加载图片。

```csharp
private static ImageSource CreateBitmapImage(Stream sourceStream, int width, int height)
{
    var bitmap = new BitmapImage();
    bitmap.BeginInit();
    bitmap.StreamSource = sourceStream;
    bitmap.DecodePixelWidth = width / monitorDpi.FactorX;
    bitmap.DecodePixelHeight = height / monitorDpi.FactorY;
    bitmap.EndInit();
}
```

在 `BitmapImage` 中，`EndInit` 调用之前是无法得知图片的像素尺寸的，所以方法必须要求传入期望的图片尺寸。要么两次加载 `BitmapImage`，要么通过其他方式来获取尺寸。

比如：

- [c# - Getting image dimensions without reading the entire file - Stack Overflow](https://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file) --&gt;

&lt;h2 id=&quot;drawingimage&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingImage&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingImage&lt;/code&gt; 可以使用 WPF 的方式来绘制，不过如果要绘制位图，也需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitmapImage&lt;/code&gt; 对象，不过这个时候我们可以按照我们需要的尺寸进行绘制而不用关心 DPI 的问题。由于尺寸是在绘制的时候确定的，所以不需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt; 控件也设置尺寸。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageSource&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateBitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StreamSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndInit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ImageDrawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorDpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FactorX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorDpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FactorY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DrawingImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ## `Bitmap` / `WriteableBitmap` --&gt;
</description>
        <pubDate>Wed, 08 Jan 2020 04:57:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-wpf-image-source-ignoring-dpi.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-wpf-image-source-ignoring-dpi.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>为什么不应该公开用来同步的加锁对象？为什么不应该 lock(this)/lock(string) 或者 lock 任何非私有对象？</title>
        <description>&lt;p&gt;如果你编写线程安全代码时为了省事儿直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock(this)&lt;/code&gt;，或者早已听说不应该 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock(this)&lt;/code&gt;，只是不知道原因，那么阅读本文可以帮助你了解原因。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;不应该 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock(this)&lt;/code&gt; 是因为你永远不知道别人会如何使用你的对象，永远不知道别人会在哪里加锁。于是稍不注意就可能死锁！&lt;/p&gt;

&lt;h2 id=&quot;实例&quot;&gt;实例&lt;/h2&gt;

&lt;p&gt;看看下面的两段代码。&lt;/p&gt;

&lt;p&gt;第一段是定义好的一个类，其中某个方法为了线程安全加了锁，但加锁的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 对象。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSafety&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 执行一些线程安全的事情。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第二段代码使用了这个类的一个实例。为了响应放到了后台线程中，但为了线程安全，加了锁。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DouB_Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSafety&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;仔细看看这段代码，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;DouB_Walterlv&lt;/code&gt; 方法执行，会发生什么？&lt;/p&gt;

&lt;p&gt;—— &lt;strong&gt;死锁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DouB_Walterlv&lt;/code&gt; 方法中完全看不出来为什么死锁，只能进入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSafety&lt;/code&gt; 中才发现试图 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 对象刚刚在另一个线程被 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock (_foo)&lt;/code&gt; 了。&lt;/p&gt;

&lt;h2 id=&quot;扩展&quot;&gt;扩展&lt;/h2&gt;

&lt;p&gt;从以上的例子可以看出，不止是 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock (this)&lt;/code&gt; 会出现“难以捉摸”的死锁问题，&lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 任何公开对象都会这样。&lt;/p&gt;

&lt;h3 id=&quot;lock-公开的属性&quot;&gt;lock 公开的属性&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyncRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只要在 A 处 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 这个对象的同时，在另一个线程调用了同样 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 这个对象的 B 处的代码，必然死锁。&lt;/p&gt;

&lt;p&gt;如果你试图实现某些接口中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyncRoot&lt;/code&gt; 属性，却遇到了上述矛盾（这样的写法不安全），那么可以阅读我的另一篇博客了解如何实现这样的“有问题”的接口：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/sync-root-on-collections&quot;&gt;为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性？如何正确实现这个属性？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lock-字符串&quot;&gt;lock 字符串&lt;/h3&gt;

&lt;p&gt;你可以定义一个私有的字符串，但你永远不知道这个字符串是否与其他字符串是同一个实例。因此这也是不安全的。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/string-intern-pool&quot;&gt;.NET/C# 的字符串暂存池 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/same-strings-at-compile-time-are-the-same-instances-at-runtime&quot;&gt;.NET/C# 编译期间能确定的相同字符串，在运行期间是相同的实例 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/compile-time-strings-are-in-the-string-intern-pool&quot;&gt;.NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lock-其他任何可能被其他对象获取的公开对象&quot;&gt;lock 其他任何可能被其他对象获取的公开对象&lt;/h3&gt;

&lt;p&gt;比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type&lt;/code&gt; 对象，比如其他公共静态对象。&lt;/p&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;

&lt;p&gt;所以，一旦你决定 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt;，那么这个对象请做成 &lt;code class=&quot;highlighter-rouge&quot;&gt;private&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Jan 2020 01:29:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/why-making-the-sync-root-public-is-dangerous.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/why-making-the-sync-root-public-is-dangerous.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何在旧版本的 .NET Core / Framework 中使用 C# 8 的异步流（IAsyncDisposable / IAsyncEnumerable / IAsyncEnumerator）</title>
        <description>&lt;p&gt;C# 8.0 为我们带来了异步流，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async foreach&lt;/code&gt;，不过使用此语法需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;IAsyncEnumerable&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;IAsyncEnumerator&lt;/code&gt; 类型。本文介绍如何在旧版本的 .NET Framework 和旧版本的 .NET Core 中获得此类型。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;异步流所需版本&quot;&gt;异步流所需版本&lt;/h2&gt;

&lt;p&gt;异步流需要 .NET Core 3.0 及以上版本才能直接支持。而如果是 .NET Framework，则是任何版本都不直接支持。&lt;/p&gt;

&lt;p&gt;如果需要在早期版本使用异步流，需要安装 Microsoft.Bcl.AsyncInterfaces 这个 NuGet 包。这就像在早期版本中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ValueTuple&lt;/code&gt; 需要安装 &lt;a href=&quot;https://www.nuget.org/packages/System.ValueTuple/&quot;&gt;System.ValueTuple&lt;/a&gt; 一样。&lt;/p&gt;

&lt;h2 id=&quot;安装-microsoftbclasyncinterfaces&quot;&gt;安装 Microsoft.Bcl.AsyncInterfaces&lt;/h2&gt;

&lt;p&gt;需要先在你的项目中安装 NuGet 包：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces/&quot;&gt;Microsoft.Bcl.AsyncInterfaces&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Bcl.AsyncInterfaces&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.1.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装此包之后，即可在你的项目当中开启异步流的支持。&lt;/p&gt;

&lt;p&gt;一点说明：异步流中使用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ValueTask&lt;/code&gt;，此类型需要 &lt;a href=&quot;https://www.nuget.org/packages/System.Threading.Tasks.Extensions&quot;&gt;System.Threading.Tasks.Extensions&lt;/a&gt; 包的支持。在 .NET Framework 4.8 以下会自动额外引入此包。&lt;/p&gt;

&lt;h2 id=&quot;使用异步流&quot;&gt;使用异步流&lt;/h2&gt;

&lt;h3 id=&quot;定义支持异步流的方法&quot;&gt;定义支持异步流的方法&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumerateTestsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;欢迎访问吕毅的博客，第 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 页&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;使用-await-foreach&quot;&gt;使用 await foreach&lt;/h3&gt;

&lt;p&gt;直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await foreach&lt;/code&gt; 即可使用 C# 8.0 带来的异步流。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumerateTestsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;额外说明&quot;&gt;额外说明&lt;/h2&gt;

&lt;p&gt;记得如果你在 .NET Framework 4.8 或以下版本，.NET Core 3.0 以下版本编写代码时，自动启用的 C# 语言版本是 7.3，所以你需要额外为你的项目启用 C# 8.0 才行。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;ValueTask&lt;/code&gt; 要求的最低 .NET Framework 版本为 4.5.2，所以如果使用更低版本的 .NET Framework，将无法使用异步流。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/async-streams&quot;&gt;Async streams - C# 8.0 specification proposals - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.strathweb.com/2019/11/using-async-disposable-and-async-enumerable-in-frameworks-older-than-net-core-3-0/&quot;&gt;Using async disposable and async enumerable in frameworks older than .NET Core 3.0 - StrathWeb. A free flowing web tech monologue.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 03 Jan 2020 09:17:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-async-streams-in-old-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-async-streams-in-old-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 解压 Zip 文件时出现异常：System.IO.InvalidDataException: 找不到中央目录结尾记录。</title>
        <description>&lt;p&gt;在解压 Zip 文件时出现异常：&lt;code class=&quot;highlighter-rouge&quot;&gt;System.IO.InvalidDataException: 找不到中央目录结尾记录。&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;其原因是所解压的文件并非 zip 文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;异常&quot;&gt;异常&lt;/h2&gt;

&lt;p&gt;在解压 Zip 文件时出现异常：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.IO.InvalidDataException: 找不到中央目录结尾记录。
   在 System.IO.Compression.ZipArchive.ReadEndOfCentralDirectory()
   在 System.IO.Compression.ZipArchive.Init(Stream stream, ZipArchiveMode mode, Boolean leaveOpen)
   在 System.IO.Compression.ZipArchive..ctor(Stream stream, ZipArchiveMode mode, Boolean leaveOpen, Encoding entryNameEncoding)
   在 System.IO.Compression.ZipFile.Open(String archiveFileName, ZipArchiveMode mode, Encoding entryNameEncoding)
   在 System.IO.Compression.ZipFile.ExtractToDirectory(String sourceArchiveFileName, String destinationDirectoryName, Encoding entryNameEncoding)
   在 System.IO.Compression.ZipFile.ExtractToDirectory(String sourceArchiveFileName, String destinationDirectoryName)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;如果一个文件并非 zip 文件，那么在解压的时候就会出现此异常。例如，它下载不全，是损坏的；或者，它实际上是一个 rar 文件或者 7z 文件。&lt;/p&gt;

&lt;p&gt;验证也非常简单，直接使用其他任何成熟的解压缩工具试着解压以下这个文件就可以。如果其他工具也不能解压，通常说明文件下载不全或者已损坏，或者下载的是一个被重定向了的 html 文件。如果其他工具能够正常解压，说明这可能是其他格式的压缩包，而不是 zip。&lt;/p&gt;
</description>
        <pubDate>Fri, 03 Jan 2020 09:12:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/zip-extracting-with-invalid-data-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/zip-extracting-with-invalid-data-exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 系统文件资源管理器的命令行参数（如何降权打开程序，如何选择文件）</title>
        <description>&lt;p&gt;大多数用户还是习惯使用 Windows 自带的文件资源管理器来管理文件，于是我们可以利用它的命令行参数来帮助我们做一些与之相关的交互。&lt;/p&gt;

&lt;p&gt;本文会以实际的例子来说明如何使用 explorer.exe 的命令行参数。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;打开文件&quot;&gt;打开文件&lt;/h2&gt;

&lt;p&gt;在命令行中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;explorer D:\Services\blog.walterlv.com\test.txt&lt;/code&gt; 即可打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;test.txt&lt;/code&gt; 文件。不过，这个时候是间接使用文件资源管理器打开的文件，效果跟我们直接在文件资源管理器中双击打开这个文件的效果是一样的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-02-08-17-31.png&quot; alt=&quot;使用文件资源管理器打开文件&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;启动程序&quot;&gt;启动程序&lt;/h2&gt;

&lt;p&gt;实际上利用文件资源管理器启动程序和前面的打开文件是同一种命令，不过我特别拿出来说，是因为使用这种方式来启动程序还有一种特别的功效：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;可以降权执行&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用方法：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Services\blog.walterlv.com\Walterlv.Blog.Home.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你当前进程是管理员权限，那么可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;explorer&lt;/code&gt; 间接启动将新启动的进程降低到与 &lt;code class=&quot;highlighter-rouge&quot;&gt;explorer&lt;/code&gt; 同级别的权限。&lt;/p&gt;

&lt;p&gt;不过，有几点需要注意的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果用来降权，那么只会降到与文件资源管理器同级别的权限
    &lt;ul&gt;
      &lt;li&gt;而文件资源管理器是什么权限在 Windows 7 上和 Windows 8/8.1/10 上不同&lt;/li&gt;
      &lt;li&gt;Windows 8/8.1/10 无论开关 UAC 都是普通用户权限，除非你特别使用任务管理器（Task Manager）以管理员权限启动文件资源管理器&lt;/li&gt;
      &lt;li&gt;Windows 7 在开启 UAC 的情况下，文件资源管理器是以普通用户权限运行的&lt;/li&gt;
      &lt;li&gt;Windows 7 在关闭 UAC 的情况下，文件资源管理器是以管理员权限运行的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;不允许给间接启动的程序携带命令行参数
    &lt;ul&gt;
      &lt;li&gt;如果你试图传入额外的参数，那么最终不会执行这个程序，只会打开一个根你的程序毫无关系的文件管理器的新窗口而已&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;explorer&lt;/code&gt; 必须是已经启动的状态（大多数时候都是这样）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于利用文件资源管理器降权执行程序的内容，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/start-process-with-lowered-uac-privileges.html&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 UAC 权限相关的内容，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/windows-user-account-control.html&quot;&gt;Windows 中的 UAC 用户账户控制 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/there-are-only-two-settings-for-the-uac-slider.html&quot;&gt;Windows 的 UAC 设置中的通知等级实际上只有两个档而已 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你使用 .NET 程序来完成启动程序的话，可能需要关注 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute&lt;/code&gt;。不过利用 &lt;code class=&quot;highlighter-rouge&quot;&gt;explorer&lt;/code&gt; 间接启动就无所谓了，无脑设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 就好，因为它自己就相当于 Shell。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/use-shell-execute-in-process-start-info.html&quot;&gt;C#/.NET 中启动进程时所使用的 UseShellExecute 设置为 true 和 false 分别代表什么意思？ - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;打开某个文件夹&quot;&gt;打开某个文件夹&lt;/h2&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Services\blog.walterlv.com&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-02-08-56-00.png&quot; alt=&quot;使用文件资源管理器打开文件夹&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;选择某个文件&quot;&gt;选择某个文件&lt;/h2&gt;

&lt;p&gt;在与其他工具集成的时候，如果有需求要打开某个文件夹，并自动滚动到希望看到的文件选中它，那么这个命令非常有用：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\Services\blog.walterlv.com\Walterlv.Blog.Home.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这可以在打开文件资源管理器的同时，选中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Blog.Home.exe&lt;/code&gt; 文件，并将它滚动到可视区域。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-02-10-21-03.png&quot; alt=&quot;使用文件资源管理器选中文件&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其他命令行参数&quot;&gt;其他命令行参数&lt;/h2&gt;

&lt;p&gt;在以上这些命令的基础上，可以添加一些可选参数用来控制如何执行这些命令。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/separate&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;让文件资源管理器在一个新的进程中打开&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;一些特殊文件夹的命令&quot;&gt;一些特殊文件夹的命令&lt;/h2&gt;

&lt;p&gt;打开当前工作路径的根目录：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2020-01-02-10-49-33.png&quot; alt=&quot;打开根目录&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开“文档”文件夹：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 或者&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;打开“计算机”文件夹：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 注意，此命令在 CMD 中可以直接执行，在 PowerShell 中需要加上引号，即 &quot;,&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;explorer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/florinlazar/2005/09/17/how-to-run-windows-explorer-as-a-different-user-so-i-can-do-admin-work/&quot;&gt;How to run Windows Explorer as a different user (so I can do admin work) – Florin Lazar – Consistency Checkpoint&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/a/591082/940098&quot;&gt;How to launch Windows Explorer with the privileges of a different domain user? - Super User&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 02 Jan 2020 03:05:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/command-line-usage-of-windows-explorer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/command-line-usage-of-windows-explorer.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何在 MSBuild 中正确使用 % 来引用每一个项（Item）中的元数据</title>
        <description>&lt;p&gt;MSBuild 中写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup /&amp;gt;&lt;/code&gt; 中的每一项是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Item&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Item&lt;/code&gt; 除了可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 来增删之外，还可以定义其他的元数据（Metadata）。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 可以引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Item&lt;/code&gt; 的元数据，本文将介绍如何正确使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 来引用每一个项中的元数据。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;定义-item-的元数据&quot;&gt;定义 Item 的元数据&lt;/h2&gt;

&lt;p&gt;就像下面这样，当引用一个 NuGet 包时，可以额外使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 来指定应该使用哪个特定版本的 NuGet 包。这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrivateAssets&lt;/code&gt; 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 的元数据。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnetCampus.Configurations.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnetCampus.CommandLine.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Win32.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.12.2-alpha&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.IO.PackageManagement.Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.13.2-alpha&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们随便创建一个新的 Item，也可以定义自己的元数据。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅的博客&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;引用元数据&quot;&gt;引用元数据&lt;/h2&gt;

&lt;p&gt;引用元数据使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 符号。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅的博客&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvItem)：%(Url)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;虽然这里我们只写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; Task，但是最终我们会输出两次，每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvItem&lt;/code&gt; 项都会输出一次。下面是这段代码的输出：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_WalterlvDemo:
  欢迎访问：https://
  吕毅的博客：blog.walterlv.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当你使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 的时候，会为每一个项执行一次这行代码。当然，如果某个 Task 支持传入集合，那么则可以直接收到集合。&lt;/p&gt;

&lt;p&gt;如果你不是用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt;，而是定义一个其他的属性，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_WalterlvItem)：%(Url)&lt;/code&gt; 作为属性的值，那么这个属性也会为每一个项都计算一次值。当然最终这个属性的值就是最后一项计算所得的值。&lt;/p&gt;

&lt;p&gt;也许可以帮你回忆一下，如果我们不写 &lt;code class=&quot;highlighter-rouge&quot;&gt;%(Url)&lt;/code&gt; 会输出什么。当只输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(WalterlvItem)&lt;/code&gt; 的时候，会以普通的分号分隔的文字。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅的博客&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvItem)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;会输出：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_WalterlvDemo:
  欢迎访问;吕毅的博客
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用元数据&quot;&gt;使用元数据&lt;/h2&gt;

&lt;p&gt;如果你希望自己处理编译过程，那么可能会对元数据做更多的处理。&lt;/p&gt;

&lt;p&gt;为了简单说明 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 的用法，我将已收集到的所有的元数据和它的本体一起输出到一个文件中。这样，后续的编译过程可以直接使用这个文件来获得所有的项和你希望关心它的所有元数据。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvContentArgsFilePath&amp;gt;&lt;/span&gt;$(IntermediateOutputPath)Args\Content.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvContentArgsFilePath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToolFile&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\compile.exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvContentArgsFilePath&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvContentFileLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Content)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Line=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Content)|%(Content.PublishState)|%(Content.CopyToOutputDirectory)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WriteLinesToFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvContentArgsFilePath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Lines=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(_WalterlvContentFileLine.Line)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Overwrite=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ConsoleToMSBuild=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;quot;$(_WalterlvToolFile)&amp;amp;quot; PackContent --content-file &amp;amp;quot; $(_WalterlvContentArgsFilePath) &amp;amp;quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的含义是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;定义一个文件路径，这个路径即将用来存放所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content&lt;/code&gt; 项和它的元数据；&lt;/li&gt;
  &lt;li&gt;定义一个工具路径，我们即将运行这个路径下的命令行程序来执行自定义的编译；&lt;/li&gt;
  &lt;li&gt;收集所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content&lt;/code&gt; 项，然后把所有项中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishState&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyToOutputDirectory&lt;/code&gt; 一起拼接成这个样子：
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Content|PublishState|CopyToOutputDirectory&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;写文件，将以上拼接出来的每一项写入到文件中的每一行；&lt;/li&gt;
  &lt;li&gt;执行工具程序，这个程序将使用这个文件来执行自定义的编译。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于使用 exe 进行自定义编译的部分可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/create-a-cross-platform-command-based-nuget-tool.html&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于写文件的部分可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/msbuild-file-and-directory-operations.html&quot;&gt;在 MSBuild 编译过程中操作文件和文件夹（检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;关于项元数据的其他信息&quot;&gt;关于项元数据的其他信息&lt;/h2&gt;

&lt;p&gt;一些已知的元数据：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata?view=vs-2019&quot;&gt;MSBuild Well-known Item Metadata - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-items&quot;&gt;MSBuild Items - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 27 Dec 2019 09:45:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-reference-msbuild-item-metadata.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-reference-msbuild-item-metadata.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在 MSBuild 编译过程中操作文件和文件夹（检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹）</title>
        <description>&lt;p&gt;本文整理 MSBuild 在编译过程中对文件和文件夹处理的各种自带的编译任务（Task）。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;exists-检查文件存在&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exists&lt;/code&gt; 检查文件存在&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exists&lt;/code&gt; 可以判断一个文件或者文件夹是否存在。注意无论是文件还是文件夹，只要给定的路径存在就返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。可以作为 MSBuild 属性、项和编译任务的执行条件。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; Exists( '$(MSBuildThisFileDirectory)..\build\build.xml' ) &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;makedir-创建文件夹&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MakeDir&lt;/code&gt; 创建文件夹&lt;/h2&gt;

&lt;p&gt;下面的例子演示创建一个文件夹：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCreateDirectoryForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MakeDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面是使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeDir&lt;/code&gt; 全部属性的例子，将已经成功创建的文件夹提取出来。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCreateDirectoryForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MakeDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DirectoriesCreated&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CreatedPackingDirectory&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MakeDir&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;move-移动文件&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 移动文件&lt;/h2&gt;

&lt;p&gt;下面的例子是将输出文件移动到一个专门的目录中，移动后，所有的文件将平级地在输出文件夹中（即所有的子文件夹中的文件也都被移动到同一层目录中了）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvMoveFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToMoveFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Move&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToMoveFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvPackingDirectory)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以通过下面的例子了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 的其他大多数属性及其用法：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvMoveFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToMoveFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvTargetFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvPackingDirectory)\%(_WalterlvToMoveFile.RecursiveDir)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Move&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToMoveFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DestinationFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvTargetFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;OverwriteReadOnlyFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MovedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MovedOutputFiles&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Copy&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码除了没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DestinationFolder&lt;/code&gt; 之外，使用到了所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 能用的属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;将所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvToCopyFile&lt;/code&gt; 一对一地复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvTargetFile&lt;/code&gt; 指定的路径上。&lt;/li&gt;
  &lt;li&gt;即便目标文件是只读的，也会覆盖。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;copy-复制文件&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Copy&lt;/code&gt; 复制文件&lt;/h2&gt;

&lt;p&gt;下面的例子是将输出文件拷贝到一个专门的目录中，保留原来所有文件之间的目录结构，并且如果文件没有改变则跳过。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCopyFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToCopyFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToCopyFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvPackingDirectory)\%(RecursiveDir)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你希望复制后所有的文件都在同一级文件夹中，不再有子文件夹，那么去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;\%(RecursiveDir)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你可以通过下面的例子了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Copy&lt;/code&gt; 的其他大多数属性及其用法：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvPackingDirectory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCopyFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToCopyFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvTargetFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvPackingDirectory)\%(_WalterlvToCopyFile.RecursiveDir)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToCopyFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DestinationFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvTargetFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;OverwriteReadOnlyFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Retries=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;RetryDelayMilliseconds=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;UseHardlinksIfPossible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CopiedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CopiedOutputFiles&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Copy&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码除了没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DestinationFolder&lt;/code&gt; 之外，使用到了所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Copy&lt;/code&gt; 能用的属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;将所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvToCopyFile&lt;/code&gt; 一对一地复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvTargetFile&lt;/code&gt; 指定的路径上。&lt;/li&gt;
  &lt;li&gt;即便目标文件是只读的，也会覆盖。&lt;/li&gt;
  &lt;li&gt;如果复制失败，则重试 10 次，每次等待 10 毫秒&lt;/li&gt;
  &lt;li&gt;如果文件没有改变，则跳过复制&lt;/li&gt;
  &lt;li&gt;如果目标文件系统支持硬连接，则使用硬连接来提升性能&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;delete-删除文件&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Delete&lt;/code&gt; 删除文件&lt;/h2&gt;

&lt;p&gt;下面这个例子是删除输出目录下的所有的 pdb 文件（适合 release 下发布软件）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDeleteFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Delete&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Files=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)*.pdb&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以把此操作已经删除的文件列表拿出来。使用全部属性的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delete&lt;/code&gt; 的例子：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDeleteFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Delete&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Files=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)*.pdb&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TreatErrorsAsWarnings=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DeletedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DeletedPdbFiles&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Delete&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;readlinesfromfile-读取文件&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ReadLinesFromFile&lt;/code&gt; 读取文件&lt;/h2&gt;

&lt;p&gt;在编译期间，可以从文件中读出文件的每一行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToWriteFile&amp;gt;&lt;/span&gt;$(OutputPath)walterlv.md&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvToWriteFile&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvReadFilesToLines&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ReadLinesFromFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvToWriteFile)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Lines&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TheLinesThatRead&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ReadLinesFromFile&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;writelinestofile-写入文件&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WriteLinesToFile&lt;/code&gt; 写入文件&lt;/h2&gt;

&lt;p&gt;可以在编译期间，将一些信息写到文件中以便后续编译的时候使用，甚至将代码写到文件中以便动态生成代码。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvBlogSite&amp;gt;&lt;/span&gt;https://blog.walterlv.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvBlogSite&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToWriteFile&amp;gt;&lt;/span&gt;$(OutputPath)walterlv.md&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvToWriteFile&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToWriteLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This is the first line&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToWriteLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This is the second line&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToWriteLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My blog site is: $(_WalterlvBlogSite)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvWriteFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WriteLinesToFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvToWriteFile)&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;Lines=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToWriteLine)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 注意，默认写入文件是不会覆盖的，会将内容补充到原来文件的后面。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvWriteFilesForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WriteLinesToFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvToWriteFile)&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;Lines=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToWriteLine)&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;Overwrite=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;Encoding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unicode&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;WriteOnlyWhenDifferent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;removedir-删除文件夹&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveDir&lt;/code&gt; 删除文件夹&lt;/h2&gt;

&lt;p&gt;在编写编译命令的时候，可能会涉及到清理资源。或者为了避免无关文件的影响，在编译之前删除我们的工作目录。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvRemoveDirectoryForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RemoveDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面是使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeDir&lt;/code&gt; 全部属性的例子，将已经成功创建的文件夹提取出来。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvRemoveDirectoryForPacking&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RemoveDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\bin\$(Configuration)\&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RemovedDirectories&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RemovedPackingDirectory&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/RemoveDir&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 27 Dec 2019 09:34:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-file-and-directory-operations.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-file-and-directory-operations.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何在 .NET/C# 代码中安全地结束掉一个控制台应用程序？通过发送 Ctrl+C 信号来结束</title>
        <description>&lt;p&gt;我的电脑上每天会跑一大堆控制台程序，于是管理这些程序的运行就成了一个问题。或者说你可能也在考虑启动一个控制台程序来完成某些特定的任务。&lt;/p&gt;

&lt;p&gt;如果我们需要结束掉这个控制台程序怎么做呢？直接杀进程吗？这样很容易出问题。我正在使用的一个控制台程序会写文件，如果直接杀进程可能导致数据没能写入到文件。所以本文介绍如何使用 .NET/C# 代码向控制台程序发送 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 来安全地结束掉程序。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;用-ctrlc-结束控制台程序&quot;&gt;用 Ctrl+C 结束控制台程序&lt;/h2&gt;

&lt;p&gt;如果直接用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Kill&lt;/code&gt; 杀掉进程，进程可能来不及保存数据。所以无论是窗口程序还是控制台程序，最好都让控制台程序自己去关闭。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-26-14-04-19.png&quot; alt=&quot;Process.Kill 结束控制台程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Kill&lt;/code&gt; 结束程序，程序退出代码是 -1&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-26-14-02-15.png&quot; alt=&quot;Ctrl+C 结束控制台程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 结束程序，程序退出代码是 0&lt;/p&gt;

&lt;h2 id=&quot;ctrlc-信号&quot;&gt;Ctrl+C 信号&lt;/h2&gt;

&lt;p&gt;Windows API 提供了方法可以将当前进程与目标控制台进程关联起来，这样我们便可以向自己发送 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 信号来结束掉关联的另一个控制台进程。&lt;/p&gt;

&lt;p&gt;关联和取消关联的方法是下面这两个，&lt;code class=&quot;highlighter-rouge&quot;&gt;AttachConsole&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FreeConsole&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AttachConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreeConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，当发送 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 信号的时候，不止我们希望关闭的控制台程序退出了，我们自己程序也是会退出的（即便我们自己是一个 GUI 程序）。所以我们必须先组织自己响应 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 信号。&lt;/p&gt;

&lt;p&gt;需要用到另外一个 API：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetConsoleCtrlHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsoleCtrlDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HandlerRoutine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CTRL_C_EVENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CTRL_BREAK_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CTRL_CLOSE_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CTRL_LOGOFF_EVENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CTRL_SHUTDOWN_EVENT&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConsoleCtrlDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CtrlType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，因为我们实际上并不需要真的对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 进行响应，只是单纯临时禁用以下，所以我们归这个委托传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 就好了。&lt;/p&gt;

&lt;p&gt;最后，也是最关键的，就是发送 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 信号了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MarshalAs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnmanagedType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateConsoleCtrlEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwCtrlEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwProcessGroupId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面，我将完整的代码贴出来。&lt;/p&gt;

&lt;h2 id=&quot;全部源代码&quot;&gt;全部源代码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Fracture.Utils&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供与控制台程序的交互。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsoleInterop&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 关闭控制台程序。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;process&quot;&amp;gt;要关闭的控制台程序的进程实例。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;timeoutInMilliseconds&quot;&amp;gt;如果不希望一直等待进程自己退出，则可以在此参数中设置超时。你可以在超时未推出候采取强制杀掉进程的策略。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果进程成功退出，则返回 true；否则返回 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StopConsoleProgram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeoutInMilliseconds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasExited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 尝试将我们自己的进程附加到指定进程的控制台（如果有的话）。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AttachConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 我们自己的进程需要忽略掉 Ctrl+C 信号，否则自己也会退出。&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetConsoleCtrlHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// 将 Ctrl+C 信号发送到前面已关联（附加）的控制台进程中。&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;GenerateConsoleCtrlEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CTRL_C_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// 拾前面已经附加的控制台。&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;FreeConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasExited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 由于 Ctrl+C 信号只是通知程序关闭，并不一定真的关闭。所以我们等待一定时间，如果仍未关闭，则超时不处理。&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 业务可以通过判断返回值来角是否进行后续处理（例如强制杀掉）。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeoutInMilliseconds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 如果没有超时处理，则一直等待，直到最终进程停止。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitForExit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;hasExited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 如果有超时处理，则超时候返回。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;hasExited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitForExit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeoutInMilliseconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// 重新恢复我们自己的进程对 Ctrl+C 信号的响应。&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetConsoleCtrlHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasExited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AttachConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreeConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetConsoleCtrlHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsoleCtrlDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HandlerRoutine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MarshalAs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnmanagedType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateConsoleCtrlEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwCtrlEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwProcessGroupId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CTRL_C_EVENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CTRL_BREAK_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CTRL_CLOSE_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CTRL_LOGOFF_EVENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CTRL_SHUTDOWN_EVENT&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConsoleCtrlDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CtrlTypes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CtrlType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何使用&quot;&gt;如何使用&lt;/h2&gt;

&lt;p&gt;现在，我们可以通过调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConsoleInterop.StopConsoleProgram(process)&lt;/code&gt; 来安全地结束掉一个控制台程序。&lt;/p&gt;

&lt;p&gt;当然，为了处理一些意外的情况，我把超时也加上了。下面的用法演示超时 2 秒候程序还没有退出，则强杀。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsoleInterop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StopConsoleProgram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Kill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-26-14-02-15.png&quot; alt=&quot;Ctrl+C 结束控制台程序&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/15281070/6233938&quot;&gt;signals - Can I send a ctrl-C (SIGINT) to an application on Windows? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/&quot;&gt;Stopping command-line applications programatically with Ctrl-C event from .Net – a working demo - Nemo’s Realms&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/console/attachconsole&quot;&gt;AttachConsole function - Windows Console - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/console/setconsolectrlhandler&quot;&gt;SetConsoleCtrlHandler function - Windows Console - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 26 Dec 2019 06:16:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/shutdown-a-console-program-safely-using-ctrl-c.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/shutdown-a-console-program-safely-using-ctrl-c.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何将一个 .NET 对象序列化为 HTTP GET 的请求字符串</title>
        <description>&lt;p&gt;HTTP GET 请求时携带的参数直接在 URL 中，形式如 &lt;code class=&quot;highlighter-rouge&quot;&gt;?key1=value&amp;amp;key2=value&amp;amp;key3=value&lt;/code&gt;。如果是 POST 请求时，我们可以使用一些库序列化为 json 格式作为 BODY 发送，那么 GET 请求呢？有可以直接将其序列化为 HTTP GET 请求的 query 字符串的吗？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;http-get-请求&quot;&gt;HTTP GET 请求&lt;/h2&gt;

&lt;p&gt;一个典型的 HTTP GET 请求带参数的话大概是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://s.blog.walterlv.com/api/example?key1=value&amp;amp;key2=value&amp;amp;key3=value
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们将一个类型序列化为后面的参数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataContract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;key1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;key2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;key3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;库&quot;&gt;库？&lt;/h2&gt;

&lt;p&gt;可能是这个需求太简单了，所以并没有找到单独的库。所以我就写了一个源代码包放到了 nuget.org 上。&lt;/p&gt;

&lt;p&gt;在这里下载源代码包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.Web.Source/&quot;&gt;Walterlv.Web.Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你不需要担心引入额外的依赖，因为这是一个源代码包。关于源代码包不引入额外依赖 dll 的原理，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-merge-dotnet-assemblies&quot;&gt;.NET 将多个程序集合并成单一程序集的 4+3 种方法 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;方法&quot;&gt;方法&lt;/h2&gt;

&lt;p&gt;我们需要做的是，将一个对象序列化为 query 字符串。假设这个对象的局部变量名称是 &lt;code class=&quot;highlighter-rouge&quot;&gt;query&lt;/code&gt;，于是我们需要：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;取得此对象所有可获取值的属性
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;query.GetType().GetProperties()&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;获取此属性值的方法
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;property.GetValue(query, null)&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;将属性和值拼接起来
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;string.Join(&quot;&amp;amp;&quot;, properties)&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然而真实场景可能比这个稍微复杂一点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们需要像 Newtonsoft.Json 一样，对于标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataContract&lt;/code&gt; 的类，按照 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataMember&lt;/code&gt; 来序列化&lt;/li&gt;
  &lt;li&gt;URL 中的值需要进行转义&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，我写出了下面的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataContractAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanRead&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataMemberAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataMemberAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;=&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpUtility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UrlEncode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;完整的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.Serialization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Web.Core&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;QueryString&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotNullIfNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataContractAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                             &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanRead&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataMemberAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isContractedType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataMemberAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;
                             &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                             &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;=&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpUtility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UrlEncode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queryString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;[return: NotNullIfNotNull(&quot;query&quot;)]&lt;/code&gt; 这一行编译不通过的情况，这个是 C# 8.0 带的可空引用类型所需要的契约类。&lt;/p&gt;

&lt;p&gt;你可以将它删除，或者安装我的另一个 NuGet 包来获得更多可空引用类型契约的支持，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/csharp-nullable-analysis-attributes&quot;&gt;C# 8.0 的可空引用类型，不止是加个问号哦！你还有很多种不同的可空玩法 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 23 Dec 2019 10:45:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/serialize-object-to-http-get-query-string.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/serialize-object-to-http-get-query-string.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>屏幕边缘上有趣的 1 个像素，看不见、摸不到</title>
        <description>&lt;p&gt;如果你的屏幕分辨率是 1920×1080，那么一个全屏的窗口程序尺寸是多少呢？想都不用想，是 1920×1080。&lt;/p&gt;

&lt;p&gt;那么输入设备输入的坐标是多少呢？是 &lt;code class=&quot;highlighter-rouge&quot;&gt;X∈[0, 1919]&lt;/code&gt; ？还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;X∈[1, 1920]&lt;/code&gt; ？还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;X∈[0, 1920]&lt;/code&gt; ？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;鼠标输入与触摸输入&quot;&gt;鼠标输入与触摸输入&lt;/h2&gt;

&lt;p&gt;一个有趣的问题，因为 1920×1080 分辨率的屏幕，其横向只有 1920 个像素，也就是说如果需要区分一个像素，那么只需要 1920 个数值就够了。这意味着 &lt;code class=&quot;highlighter-rouge&quot;&gt;X∈[0, 1919]&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;X∈[1, 1920]&lt;/code&gt; 的取值范围就能表示横向的所有像素了。&lt;/p&gt;

&lt;p&gt;那么实际上最左侧的点的输入数值是多少呢？最右侧的点的输入数值是多少呢？&lt;/p&gt;

&lt;p&gt;我写了一个最大化全屏的程序专门用来测试鼠标和触摸输入的数值是多少。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-23-15-39-01.png&quot; alt=&quot;鼠标输入&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 在鼠标输入的情况下，最右侧其实是 1919（我的屏幕是 2560×1080，所以最右侧是 2559）&lt;/p&gt;

&lt;p&gt;测量的时候，鼠标是直接往右移动到底，移到不能动为止。&lt;/p&gt;

&lt;p&gt;那么在触摸输入的时候又如何？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-23-15-47-26.png&quot; alt=&quot;触摸输入&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 在触摸输入的情况下，最右侧是 1920（我的屏幕是 2560×1080，所以最右侧是 2560）&lt;/p&gt;

&lt;p&gt;测量的时候，是让手指近乎在屏幕外触摸，不断触摸到能够在屏幕上看到的最小或最大值为止。&lt;/p&gt;

&lt;h2 id=&quot;有趣的-1-像素&quot;&gt;有趣的 1 像素&lt;/h2&gt;

&lt;p&gt;发现上面实验中有趣的现象了吗？明明只有 1920×1080 的屏幕分辨率，窗口明明只有 1920×1080 那么大，鼠标下收到正常范围内的输入坐标，而触摸下我们能收到超出我们窗口大小 1 像素的触摸事件！&lt;/p&gt;

&lt;p&gt;问题并没有完——&lt;/p&gt;

&lt;p&gt;如果说，触摸给了你超出窗口大小的坐标，那么你能如何使用这个坐标呢？虽然程序里收到什么坐标都无所谓（至少不崩），但如果你真拿它来渲染，就会在屏幕之外。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;更有趣的是&lt;/strong&gt;，虽然你能收到这个“在屏幕边缘之外”的坐标，但这个消息并不总会发送到你的程序里。更多的时候，你的程序根本就不会收到这个触摸事件，于是我们也就不能在程序里面更新窗口上显示的坐标到 1920 了，就像鼠标一样。&lt;/p&gt;

&lt;p&gt;于是，你可能遇到的问题是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果你在屏幕的左侧边缘触摸，你的程序可以一直收到触摸事件，你能够得到正确的响应；&lt;/li&gt;
  &lt;li&gt;如果你在屏幕的右侧边缘触摸，你将仅能偶尔收到零星的刚好超出窗口大小的触摸坐标，大多数时候收不到触摸事件，于是你可能无法获知用户在屏幕右侧边缘进行触摸。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;防踩坑秘籍&quot;&gt;防踩坑秘籍&lt;/h2&gt;

&lt;p&gt;林德熙小伙伴告诉我说可以特意把窗口的尺寸做大一个像素。我试过了，确实能够让触摸在整个屏幕上生效，但对于双屏用户来说，就能在另外一个屏幕上看到“露馅儿”了的窗口，对于我这种强迫症患者来说，显然是不能接受的。&lt;/p&gt;

&lt;p&gt;我的建议是，并不需要对这种情况进行什么特殊的处理。&lt;/p&gt;
</description>
        <pubDate>Mon, 23 Dec 2019 08:00:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-fantastic-one-pixel-of-the-touch-screen.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-fantastic-one-pixel-of-the-touch-screen.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用 MSBuild Target 复制文件的时候如何保持文件夹结构不变</title>
        <description>&lt;p&gt;使用 MSBuild 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Copy&lt;/code&gt; 这个编译目标可以在 .NET 项目编译期间复制一些文件。不过使用默认的参数复制的时候文件夹结构会丢失，所有的文件会保留在同一级文件夹下。&lt;/p&gt;

&lt;p&gt;那么如何在复制文件的时候保持文件夹结构与原文件夹结构一样呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;copy&quot;&gt;Copy&lt;/h2&gt;

&lt;p&gt;下面是一个典型的使用 MSBuild 在编译期间复制文件的一个编译目标。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCopyDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvToCopyFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_WalterlvToCopyFile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bin\Debug\Test&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样复制的文件是不会保留文件夹结构的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-17-16-13-21.png&quot; alt=&quot;在同一层级&quot; /&gt;&lt;/p&gt;

&lt;p&gt;复制之后，所有的文件夹将不存在，所有文件覆盖地到同一层级。&lt;/p&gt;

&lt;h2 id=&quot;recursivedir&quot;&gt;RecursiveDir&lt;/h2&gt;

&lt;p&gt;如果希望保留文件夹层级，可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DestinationFolder&lt;/code&gt; 中使用文件路径来替代文件夹路径。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Target Name=&quot;_WalterlvCopyDemo&quot; AfterTargets=&quot;AfterBuild&quot;&amp;gt;
    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;_WalterlvToCopyFile Include=&quot;$(OutputPath)**&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-   &amp;lt;Copy SourceFiles=&quot;@(_WalterlvToCopyFile)&quot; DestinationFolder=&quot;bin\Debug\Test&quot; SkipUnchangedFiles=&quot;True&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;Copy SourceFiles=&quot;@(_WalterlvToCopyFile)&quot; DestinationFolder=&quot;bin\Debug\Test\%(RecursiveDir)&quot; SkipUnchangedFiles=&quot;True&quot; /&amp;gt;
&lt;/span&gt;  &amp;lt;/Target&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-12-17-16-14-27.png&quot; alt=&quot;保留了文件夹层次结构&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 17 Dec 2019 08:36:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/copy-all-files-with-directory-hierarchy-using-msbuild.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/copy-all-files-with-directory-hierarchy-using-msbuild.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用正则表达式尽可能准确匹配域名/网址</title>
        <description>&lt;p&gt;你可能需要准确地知道一段字符串是否是域名/网址/URL。虽然可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 这些来模糊匹配，但会造成误判。&lt;/p&gt;

&lt;p&gt;实际上单纯使用正则表达式来精确匹配也是非常复杂的，通过代码来判断会简单很多。不过本文依然从域名的定义出发来尽可能匹配一段字符串是否是域名或者网址，在要求不怎么高的场合，使用本文的正则表达式写的代码会比较简单。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;网址&quot;&gt;网址&lt;/h2&gt;

&lt;p&gt;网址实际上是 URL（统一资源定位符），它是由协议、主机名和路径组成。不过我们通常所说的网址中的主机名通常是域名，因此我们在匹配的时候主要考虑域名。&lt;/p&gt;

&lt;h3 id=&quot;域名&quot;&gt;域名&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D&quot;&gt;维基百科&lt;/a&gt; 中关于域名的描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;域名由一或多个部分组成，这些部分通常连接在一起，并由点分隔。最右边的一个标签是顶级域名，例如zh.wikipedia.org的顶级域名是org。一个域名的层次结构，从右侧到左侧隔一个点依次下降一层。每个标签可以包含1到63个八字节。域名的结尾有时候还有一点，这是保留给根节点的，书写时通常省略，在查询时由软件内部补上。&lt;/li&gt;
    &lt;li&gt;域名里的英文字母不区分大小写。&lt;/li&gt;
    &lt;li&gt;完整域名的所有字符加起来不得超过253个ASCII字符的总长度。因此，当每一级都使用单个字符时，限制为127个级别：127个字符加上126个点的总长度为253。但实际上，某些域名可能具有其他限制；也没有只有一个字符的域名后缀。&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;后面关于非 ASCII 字符的描述我没有贴出来。这种域名例如“.中国”。&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;http://beian.ct10000.com/portal/icp/help/helpdetail.do?helpid=305&quot;&gt;中国电信网站备案自助管理系统&lt;/a&gt; 中，我们可以找到关于域名的描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;域名中的标号都由英文字母和数字组成，每一个标号不超过63个字符，也不区分大小写字母。标号中除连字符（-）外不能使用其他的标点符号。级别最低的域名写在最左边，而级别最高的域名写在最右边。由多个标号组成的完整域名总共不超过255个字符。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;路径&quot;&gt;路径&lt;/h3&gt;

&lt;p&gt;路径是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 分隔的一段一段字符串。&lt;/p&gt;

&lt;h2 id=&quot;正则表达式匹配&quot;&gt;正则表达式匹配&lt;/h2&gt;

&lt;p&gt;在确认了完整的网址 URL 的规范之后，使用正则表达式来匹配就会比较精确了。&lt;/p&gt;

&lt;h3 id=&quot;域名-1&quot;&gt;域名&lt;/h3&gt;

&lt;p&gt;现在，我们来尝试匹配一下域名 &lt;blog-test.walterlv.com&gt;。&lt;/blog-test.walterlv.com&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;每个标签可组成的字符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;a-z&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;A-Z&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;0-9&lt;/code&gt;，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; 不可作为开头，标签总长度 1-63 个字符，于是
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[a-zA-Z0-9][-a-zA-Z0-9]{0,62}&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;即首字不含 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;，后面的字可以包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;允许多个标签，于是
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;除了标签内容和前面一样，但我们加了个 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;别忘了，我们还有总长度限制，于是考虑加上零宽断言 &lt;code class=&quot;highlighter-rouge&quot;&gt;^.{3,255}$&lt;/code&gt;，匹配开头和结尾，中间任意字符但长度在 3-255 之间。通过零宽断言，我们可以在不捕获匹配字符串的情况下对后面的字符串增加限制条件。&lt;/p&gt;

&lt;p&gt;现在，把整个正则表达式拼出来：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;url&quot;&gt;URL&lt;/h3&gt;

&lt;p&gt;对于不同的业务需求，可能有严格匹配或者宽松的匹配方式。&lt;/p&gt;

&lt;p&gt;比如你要做一些比较精准的检查时需要进行严格的检查，那么选择严格匹配；这时，稍微出现一些不符合要求的字符都将认定为不是 URL。&lt;/p&gt;

&lt;p&gt;如果你只是打算做一些简单的检查（例如只是语法高亮），那么简单匹配即可；因为当你使用 Chrome 浏览器访问这些 URL 的时候，依然可以正常访问，Chrome 会帮你格式化一下这个 URL。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&quot;&gt;https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;严格匹配和宽松匹配都会成功匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-配置-github-自动打包上传-nuget-文件.html&quot;&gt;https://blog.lindexi.com/post/dotnet-配置-github-自动打包上传-nuget-文件.html&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;里面有 Unicode 字符，宽松匹配才可以匹配此 URL&lt;/li&gt;
      &lt;li&gt;你把这个 URL 复制到 Chrome 中可以正常打开，但从 Chrome 里把这个复制出来的话，就会被转义成 &lt;a href=&quot;https://blog.lindexi.com/post/dotnet-%E9%85%8D%E7%BD%AE-github-%E8%87%AA%E5%8A%A8%E6%89%93%E5%8C%85%E4%B8%8A%E4%BC%A0-nuget-%E6%96%87%E4%BB%B6.html&quot;&gt;https://blog.lindexi.com/post/dotnet-%E9%85%8D%E7%BD%AE-github-%E8%87%AA%E5%8A%A8%E6%89%93%E5%8C%85%E4%B8%8A%E4%BC%A0-nuget-%E6%96%87%E4%BB%B6.html&lt;/a&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://[2001:4860:4860::8888]:53/favicon.svg&quot;&gt;https://[2001:4860:4860::8888]:53/favicon.svg&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;因为我偷懒了，所以只有宽松匹配才可以匹配此 IPv6 地址下的 URL&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://域名.中国&quot;&gt;https://域名.中国&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;因为我偷懒了，所以只有宽松匹配才可以匹配此 IPv6 地址下的 URL&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;url严格&quot;&gt;URL（严格）&lt;/h4&gt;

&lt;p&gt;匹配 URL 跟匹配域名不一样，URL 复杂得多。严格匹配的要求是准确反应出 URL 的标准，但实际上如实反应标准编写的正则表达式会非常复杂，因此相比于 100% 准确匹配，我们还是从简了。&lt;/p&gt;

&lt;p&gt;所以如果不是有特别要求，建议还是跳到后面的“宽松”部分来阅读吧！&lt;/p&gt;

&lt;p&gt;我们以下面这个网址为例说明。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&quot;&gt;https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前面是可选的协议名，于是
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;(http(s)?:\/\/)&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;然而既然可选，而且是行首，那么加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; 和什么都不加的效果是一样的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;随后是域名，于是
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;这里我们没有把总长度限制算上去&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;别忘了有个可选的端口号
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;(:[0-9]{1,5})?&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;端口号的范围是 0-65535，但 0 是保留端口，49152 到 65535 也是保留端口，因此可以作为网址访问的范围也就是 1-49151，因此我们限制 1-5 位长度。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;接下来是资源路径
    &lt;ul&gt;
      &lt;li&gt;资源路径可以使用的字符也是有限制的，我们接下来详细说明。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;组合整个正则表达式：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:[0-9]{1,5})?[-a-zA-Z0-9()@:%_\\\+\.~#?&amp;amp;//=]*$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;顺便一提，不同于域名，我们这里去掉了长度限制，因为 URL 真的可以“很长”。另外，这里的&lt;/p&gt;

&lt;p&gt;现在，我们补充说明一下资源路径可以使用的字符问题。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;:&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt; 这些字符应该被转义。转义使用的字符是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;xxx;&lt;/code&gt;，因此在转义之后，依然还可能在网址中看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt;，不过没有其他字符了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;!&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;(&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;)&lt;/code&gt; 这些字符可以不进行转义，但也不建议在 URL 中使用。对于这部分，我们考虑将其匹配。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;}&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;^&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;[&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;]&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 这部分字符可能被网关当作分隔符使用，因此不建议出现在 URL 中。对于这部分，我们考虑将其匹配。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; 控制字符。使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 可以组成其他 Unicode 字符，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; 用来指代网址中的某个部分。&lt;/p&gt;

&lt;p&gt;因此，我们最终总结应该匹配的特殊字符有 &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;:&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt;。&lt;/p&gt;

&lt;h4 id=&quot;url宽松&quot;&gt;URL（宽松）&lt;/h4&gt;

&lt;p&gt;宽松一点的话，正则表达式就好写多了。&lt;/p&gt;

&lt;p&gt;这个正则表达式可以不写 &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt; 协议前缀：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;^\w+[^\s]+(\.[^\s]+){1,}$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果上下文中要求必须匹配 &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt;，则可以写：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;^(http(s)?:\/\/)\w+[^\s]+(\.[^\s]+){1,}$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html#content)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望不匹配（主要是不能匹配末尾的括号），实际匹配&lt;/li&gt;
      &lt;li&gt;在 URL 中，如果括号是成对的，则此 URL 允许以 &lt;code class=&quot;highlighter-rouge&quot;&gt;)&lt;/code&gt; 结尾，如果 URL 中括号不成对，则此 URL 不能以 &lt;code class=&quot;highlighter-rouge&quot;&gt;)&lt;/code&gt; 结尾；&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; 同理&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;https://blog.walterlv.com/post/read-32bit -registry-from-x64-process.html&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望不匹配，实际不匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;https://blog.lindexi.com/post/dotnet-配置-github-自动打包上传-nuget-文件.html&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望匹配，实际匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;https://域名.中国&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望匹配，实际匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望匹配，实际匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x&amp;lt;blog.walterlv.com/post/read-32bit-registry-from-x64-process.html&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;期望不匹配，实际匹配&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里的宽松正则表达式请小心！此正则表达式会将一段话中 URL 后面非空格的部分都算作 URL 的一部分。&lt;/p&gt;

&lt;h2 id=&quot;更多大牛匹配-url-的正则表达式&quot;&gt;更多大牛匹配 URL 的正则表达式&lt;/h2&gt;

&lt;p&gt;在 GitHub 上还有很多大牛们在写各种匹配 URL 的正则表达式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/dperini/729294&quot;&gt;regex-weburl.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最长的一个写了 1347 个字符，最短的有 38 个字符。&lt;/p&gt;

&lt;p&gt;有人将其整理成一张表格，一图说明各种正则表达式能匹配到什么程度：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://mathiasbynens.be/demo/url-regex&quot;&gt;In search of the perfect URL validation regex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://mathiasbynens.be/demo/url-regex&quot;&gt;In search of the perfect URL validation regex&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D&quot;&gt;域名 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://beian.ct10000.com/portal/icp/help/helpdetail.do?helpid=305&quot;&gt;中国电信网站备案自助管理系统&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 09 Dec 2019 08:58:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/match-web-url-using-regex.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/match-web-url-using-regex.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C# 8.0 的可空引用类型，不止是加个问号哦！你还有很多种不同的可空玩法</title>
        <description>&lt;p&gt;C# 8.0 引入了可空引用类型，你可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; 为字段、属性、方法参数、返回值等添加是否可为 null 的特性。&lt;/p&gt;

&lt;p&gt;但是如果你真的在把你原有的旧项目迁移到可空类型的时候，你就会发现情况远比你想象当中复杂，因为你写的代码可能只在部分情况下可空，部分情况下不可空；或者传入空时才可为空，传入非空时则不可为空。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;c-80-可空特性&quot;&gt;C# 8.0 可空特性&lt;/h2&gt;

&lt;p&gt;在开始迁移你的项目之前，你可能需要了解如何开启项目的可空类型支持：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-enable-nullable-reference-types&quot;&gt;C# 8.0 如何在项目中开启可空引用类型的支持 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可空引用类型是 C# 8.0 带来的新特性。&lt;/p&gt;

&lt;p&gt;你可能会好奇，C# 语言的可空特性为什么在编译成类库之后，依然可以被引用它的程序集识别。也许你可以理解为有什么特性 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 标记了字段、属性、方法参数、返回值的可空特性，于是可空特性就被编译到程序集中了。&lt;/p&gt;

&lt;p&gt;确实，可空特性是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableAttribute&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableContextAttribute&lt;/code&gt; 这两个特性标记的。&lt;/p&gt;

&lt;p&gt;但你是否好奇，即使在古老的 .NET Framework 4.5 或者 .NET Standard 2.0 中开发的时候，你也可以编译出支持可空信息的程序集出来。这些古老的框架中没有这些新出来的类型，为什么也可以携带类型的可空特性呢？&lt;/p&gt;

&lt;p&gt;实际上反编译一下编译出来的程序集就能立刻看到结果了。&lt;/p&gt;

&lt;p&gt;看下图，在早期版本的 .NET 框架中，可空特性实际上是被编译到程序集里面，作为 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 类型了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-27-16-39-21.png&quot; alt=&quot;反编译&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以，放心使用可空类型吧！旧版本的框架也是可以用的。&lt;/p&gt;

&lt;h2 id=&quot;更灵活控制的可空特性&quot;&gt;更灵活控制的可空特性&lt;/h2&gt;

&lt;p&gt;阻碍你将老项目迁移到可空类型的原因，可能还有你原来代码逻辑的问题。因为有些情况下你无法完完全全将类型迁移到可空。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有些时候你不得不为非空的类型赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 或者获取可空类型时你能确保此时一定不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;（待会儿我会解释到底是什么情况）；&lt;/li&gt;
  &lt;li&gt;一个方法，可能这种情况下返回的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 那种情况下返回的是非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;可能调用者传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的时候才返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，传入非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的时候返回非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了解决这些情况，C# 8.0 还同时引入了下面这些 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AllowNull&lt;/code&gt;: 标记一个不可空的输入实际上是可以传入 null 的。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DisallowNull&lt;/code&gt;: 标记一个可空的输入实际上不应该传入 null。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MaybeNull&lt;/code&gt;: 标记一个非空的返回值实际上可能会返回 null，返回值包括输出参数。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNull&lt;/code&gt;: 标记一个可空的返回值实际上是不可能为 null 的。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MaybeNullWhen&lt;/code&gt;: 当返回指定的 true/false 时某个输出参数才可能为 null，而返回相反的值时那个输出参数则不可为 null。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNullWhen&lt;/code&gt;: 当返回指定的 true/false 时，某个输出参数不可为 null，而返回相反的值时那个输出参数则可能为 null。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNullIfNotNull&lt;/code&gt;: 指定的参数传入 null 时才可能返回 null，指定的参数传入非 null 时就不可能返回 null。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DoesNotReturn&lt;/code&gt;: 指定一个方法是不可能返回的。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DoesNotReturnIf&lt;/code&gt;: 在方法的输入参数上指定一个条件，当这个参数传入了指定的 true/false 时方法不可能返回。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;想必有了这些描述后，你在具体遇到问题的时候应该能知道选用那个特性。但单单看到这些特性的时候你可能不一定知道什么情况下会用得着，于是我可以为你举一些典型的例子。&lt;/p&gt;

&lt;h3 id=&quot;输入allownull&quot;&gt;输入：&lt;code class=&quot;highlighter-rouge&quot;&gt;AllowNull&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;设想一下你需要写一个属性：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当你获取这个属性的值的时候，你一定不会获取到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，因为我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;get&lt;/code&gt; 里面指定了非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的默认值。然而我是允许你设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 到这个属性的，因为我处理好了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的情况。&lt;/p&gt;

&lt;p&gt;于是，请为这个属性加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowNull&lt;/code&gt;。这样，获取此属性的时候会得到非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的值，而设置的时候却可以设置成 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [AllowNull]
&lt;/span&gt;    public string Text
    {
        get =&amp;gt; GetValue() ?? &quot;&quot;;
        set =&amp;gt; SetValue(value ?? &quot;&quot;);
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;输入disallownull&quot;&gt;输入：&lt;code class=&quot;highlighter-rouge&quot;&gt;DisallowNull&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;与以上场景相反的一个场景：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;不允许将这个值设置为 null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当你获取这个属性的时候，这个属性可能还没有初始化，于是我们获取到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。然而我却并不允许你将这个属性赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，因为这是个不合理的值。&lt;/p&gt;

&lt;p&gt;于是，请为这个属性加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;DisallowNull&lt;/code&gt;。这样，获取此属性的时候会得到可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的值，而设置的时候却不允许为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;输出maybenull&quot;&gt;输出：&lt;code class=&quot;highlighter-rouge&quot;&gt;MaybeNull&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果你有尝试过迁移代码到可空类型，基本上一定会遇到泛型方法的迁移问题：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如以上这个方法，找到了就返回找到的值，找不到就返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 的默认值。那么问题来了，&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 没有指定这是值类型还是引用类型。&lt;/p&gt;

&lt;p&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 是引用类型，那么默认值 &lt;code class=&quot;highlighter-rouge&quot;&gt;default(T)&lt;/code&gt; 就会引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。但是泛型 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 并没有写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;T?&lt;/code&gt;，因此它是不可为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。然而值类型和引用类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T?&lt;/code&gt; 代表的是不同的含义。这种矛盾应该怎么办？&lt;/p&gt;

&lt;p&gt;这个时候，请给返回值标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;MaybeNull&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [return: MaybeNull]
&lt;/span&gt;    public T Find&amp;lt;T&amp;gt;(int index)
    {
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这表示此方法应该返回一个不可为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的类型，但在某些情况下可能会返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;实际上这样的写法并没有从本质上解决掉泛型 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 的问题，不过可以用来给旧项目迁移时用来兼容 API 使用。&lt;/p&gt;

&lt;p&gt;如果你可以不用考虑 API 的兼容性，那么可以使用新的泛型契约 &lt;code class=&quot;highlighter-rouge&quot;&gt;where T : notnull&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notnull&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;输出notnull&quot;&gt;输出：&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNull&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;设想你有一个方法，方法参数是可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而这个方法的语义是确保此字段初始化。于是可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 但不会返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。这个时候请标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotNull&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  public void EnsureInitialized(ref string? text)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  public void EnsureInitialized([NotNull] ref string? text)
&lt;/span&gt;    {
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;notnullwhen-maybenullwhen&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNullWhen&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;MaybeNullWhen&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;string.IsNullOrEmpty&lt;/code&gt; 的实现就使用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotNullWhen&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNullWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它表示当返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 参数是不可为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。&lt;/p&gt;

&lt;p&gt;这样，你在这个方法返回的 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 判断分支里面，是不需要对变量进行判空的。&lt;/p&gt;

&lt;p&gt;当然，更典型的还有 TryDo 模式。比如下面是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryParse&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNullWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;result&lt;/code&gt; 一定不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;notnullifnotnull&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotNullIfNotNull&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;典型的情况比如指定默认值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotNullIfNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码里面，如果指定的默认值（&lt;code class=&quot;highlighter-rouge&quot;&gt;defaultValue&lt;/code&gt;）是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 那么返回值也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；而如果指定的默认值是非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，那么返回值也就不可为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 了。&lt;/p&gt;

&lt;h2 id=&quot;在早期-net-framework-或者早期版本的-net-core-中使用&quot;&gt;在早期 .NET Framework 或者早期版本的 .NET Core 中使用&lt;/h2&gt;

&lt;p&gt;在本文第一小节里面，我们说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&lt;/code&gt; 是编译到目标程序集中的，所以不需要引用什么特别的程序集就能够使用到可空引用的特性。&lt;/p&gt;

&lt;p&gt;那么上面这些特性呢？它们并没有编译到目标程序集中怎么办？&lt;/p&gt;

&lt;p&gt;实际上，你只需要有一个命名空间、名字和实现都相同的类型就够了。你可以写一个放到你自己的程序集中，也可以把这些类型写到一个自己公共的库中，然后引用它。当然，你也可以用我已经写好的 NuGet 包 Walterlv.NullableAttributes。&lt;/p&gt;

&lt;h2 id=&quot;walterlvnullableattributes&quot;&gt;Walterlv.NullableAttributes&lt;/h2&gt;

&lt;p&gt;微软 .NET 官方的可空特性在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/NullableAttributes.cs&quot;&gt;NullableAttributes.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我将其注释翻译成中文之后，也写了一份在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/blob/master/src/Diagnostics/Walterlv.NullableAttributes/CodeAnalysis.cs&quot;&gt;Walterlv.Packages/CodeAnalysis.cs at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你想简单一点，可以直接引用我的 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;作为 dll 引用：&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.NullableAttributes&quot;&gt;NuGet Gallery - Walterlv.NullableAttributes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;作为源代码包引用：&lt;a href=&quot;https://www.nuget.org/packages/NullableAttributes.Source&quot;&gt;NuGet Gallery - Walterlv.NullableAttributes.Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;源代码包可以在不用引入其他 dll 依赖的情况下完成引用。最终你输出的程序集是不带对此包的依赖的，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-merge-dotnet-assemblies&quot;&gt;.NET 将多个程序集合并成单一程序集的 4+3 种方法 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/nullable-attributes&quot;&gt;Upgrade APIs for nullable reference types with attributes that define expectations for null values - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 08 Dec 2019 07:29:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/csharp-nullable-analysis-attributes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/csharp-nullable-analysis-attributes.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>一个简单的方法：截取子类名称中不包含基类后缀的部分</title>
        <description>&lt;p&gt;基类是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MenuItem&lt;/code&gt;，子类是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvMenuItem&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;FooMenuItem&lt;/code&gt;。基类是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configuration&lt;/code&gt;，子类是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvConfiguration&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ExtensionConfiguration&lt;/code&gt;。在代码中，我们可能会为了能够一眼看清类之间的继承（从属）关系而在子类名称后缀中带上基类的名称。但是由于这种情况下的基类不参与实际的业务，所以对外（文件/网络）的名称通常不需要带上这个后缀。&lt;/p&gt;

&lt;p&gt;本文提供一个简单的方法，让子类中基类的后缀删掉，只取得前面的那部分。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在这段代码中，我们至少需要获得两个传入的参数，一个是基类的名称，一个是子类的名称。但是考虑到让开发者就这样传入两者名称的话会比较容易出问题，因为开发者可能根本就不会按照要求去获取类型的名称。所以我们需要自己通过类型对象来获取名称。&lt;/p&gt;

&lt;p&gt;另外，我们还需要有一些约束，必须有一个类型是另外一个类型的子类。于是我们可能必须来使用泛型做这样的约束。&lt;/p&gt;

&lt;p&gt;于是，我们可以写出下面的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Utils&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含类名相关的处理方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassNameUtils&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当某个类型的派生类都以基类（&amp;lt;typeparamref name=&quot;T&quot;/&amp;gt;）名称作为后缀时，去掉后缀取派生类名称的前面部分。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;typeparam name=&quot;T&quot;&amp;gt;名称统一的基类名称。&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;this&quot;&amp;gt;派生类的实例。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;去掉后缀的派生类名称。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetClassNameWithoutSuffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 截取子类名称中去掉基类后缀的部分。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果子类名称和基类完全一样，则直接返回子类名称。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derivedTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们通过判断子类是否以基类名称作为后缀来决定是否截取子字符串。&lt;/p&gt;

&lt;p&gt;在截取完子串之后，我们还需要验证截取的字符串是否已经是空串了，因为父子类的名称可能是完全一样的（虽然这样的做法真的很逗比）。&lt;/p&gt;

&lt;p&gt;于是使用起来只需要简单调用一下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClassNameUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetClassNameWithoutSuffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XFoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;XFoo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们可以得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; 局部变量的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;X&lt;/code&gt;。如果这个时候我们对 &lt;code class=&quot;highlighter-rouge&quot;&gt;XFoo&lt;/code&gt; 类型改名，例如改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;XFoo1&lt;/code&gt;，那么就不会截取，而是直接得到名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;XFoo1&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Dec 2019 07:29:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-derived-type-name-without-base-type-name.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-derived-type-name-without-base-type-name.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>可集成到文件管理器，一句 PowerShell 脚本发布某个版本的所有 NuGet 包</title>
        <description>&lt;p&gt;要发布 NuGet 包，只需要执行命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget push xxx.nupkg&lt;/code&gt; 即可，或者去 nuget.org 点鼠标上传。&lt;/p&gt;

&lt;p&gt;不过，如果你有很多的 NuGet 包并且经常需要推送的话，也可以集成到 Directory Opus 或者 Total Commander 中。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;nuget-推送命令&quot;&gt;NuGet 推送命令&lt;/h2&gt;

&lt;p&gt;NuGet 推送命令可直接在微软官方文档中阅读到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-push&quot;&gt;NuGet CLI push command - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在你已经&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-setapikey&quot;&gt;设置了 ApiKey&lt;/a&gt; 的情况下：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setapikey&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://api.nuget.org/v3/index.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;之后你只需要执行一句命令即可：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Themes.FluentDesign.Source.0.8.0-alpha.nupkg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://api.nuget.org/v3/index.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者推送此文件夹下 0.8.0-alpha 版本的所有 NuGet 包：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.0.8.0&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nupkg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://api.nuget.org/v3/index.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;用-powershell-包装一下&quot;&gt;用 PowerShell 包装一下&lt;/h2&gt;

&lt;p&gt;要执行 NuGet 的推送命令，我们需要一个可以执行命令的终端，比如 PowerShell。命令的执行结果我们也可以直接在终端看到。&lt;/p&gt;

&lt;p&gt;不过，如果命令是集成到其他工具里面，那么就不一定能够看得到命令的执行结果了。&lt;/p&gt;

&lt;p&gt;这个时候，可以考虑用 PowerShell 间接执行这个命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PowerShell 版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;powershell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoExit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nuget push *.0.8.0-alpha.nupkg -Source https://api.nuget.org/v3/index.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PowerShell Core 版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoExit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nuget push *.0.8.0-alpha.nupkg -Source https://api.nuget.org/v3/index.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于使用 PowerShell 间接执行命令的更多细节，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/powershell-startup-arguments&quot;&gt;PowerShell 的命令行启动参数（可用于执行命令、传参或进行环境配置） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;集成到-directory-opus&quot;&gt;集成到 Directory Opus&lt;/h2&gt;

&lt;p&gt;我将这个命令集成到了 Directory Opus 中，这样，一次点击或者一个快捷键就能发布某个特定版本的所有的 NuGet 包了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-20-14-31-13.png&quot; alt=&quot;集成到 Directory Opus&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于使用 Directory Opus 继承工具栏按钮的细节，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-custom-toolbar-buttons&quot;&gt;在 Directory Opus 中添加自定义的工具栏按钮提升效率 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体来说，就是安装上文中所述的方法添加一个按钮，在按钮当中需要执行的脚本如下：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{sourcepath} &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoExit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=[Regex]::Match('{file}', '\.\d+\.\d+\.\d+.+.nupkg').Value; nuget push *&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -Source https://api.nuget.org/v3/index.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;含义为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;转到 Directory Opus 当前目录&lt;/li&gt;
  &lt;li&gt;执行一段 PowerShell 脚本，但执行完之后不退出（这样，我可以观察到我实际上推送的是哪一些包，并且可以知道推送是否出现了错误）&lt;/li&gt;
  &lt;li&gt;要执行的命令为 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget push *.xxx.nupkg -Source https://api.nuget.org/v3/index.json&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;其中，中间的 xxx 是使用正则表达式匹配的 &lt;code class=&quot;highlighter-rouge&quot;&gt;{file}&lt;/code&gt; 文件名&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{file}&lt;/code&gt; 是 Directory Opus 当前选中的文件，我用正则表达式匹配出其版本号和后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.nupkg&lt;/code&gt; 后缀&lt;/li&gt;
      &lt;li&gt;将正则表达式匹配出来的文本作为 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget push&lt;/code&gt; 的包，最终生成的命令会非常类似于本文一开始提到的命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget push *.0.8.0-alpha.nupkg -Source https://api.nuget.org/v3/index.json&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-22-14-52-06.png&quot; alt=&quot;Directory Opus 工具栏按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，当我选中了一个包，按下这个工具栏按钮之后，就可以推送与这个包相同版本的所有的 NuGet 包了。&lt;/p&gt;

&lt;p&gt;毕竟我一次编译产生的 NuGet 包太多了，还是需要使用这样的方式来提高一点效率。至于为什么不用持续集成，是因为目前 SourceYard 还不支持在 GitHub 上集成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-11-24-13-18-30.png&quot; alt=&quot;一键推送 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-push&quot;&gt;NuGet CLI push command - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 08 Dec 2019 07:29:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/push-nuget-packages-using-powershell.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/push-nuget-packages-using-powershell.html</guid>
        
        
        <category>powershell</category>
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>配置 legacyUnhandledExceptionPolicy 防止后台线程抛出的异常让程序崩溃退出</title>
        <description>&lt;p&gt;如果你的程序抛了异常，你是怎么处理的呢？等待程序崩溃退出？还是进行补救？&lt;/p&gt;

&lt;p&gt;如果是做 UI 开发，很容易就找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.UnhandledException&lt;/code&gt; 事件，然后在事件中进行补救。如果补救成功，可以设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt; 来阻止异常继续让程序崩溃退出。但是，如果是后台线程抛出了异常呢？并没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 可以用。所以我们就束手就擒让程序自己退出吗？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;WPF 和 Windows Forms 都是微软的框架，为了照顾初学者，微软会默认每一个开发者都不会正确地处理异常。于是在异常发生之后，微软 Windows 会假设开发者并不知道如何应对以便让应用程序正常工作，就擅自将应用程序进程结束掉，以便防止应用程序自己内部产生奇怪的状态和错误，避免对系统环境造成不可逆的严重后果。&lt;/p&gt;

&lt;p&gt;能够写出异常处理代码的开发者，微软会默认他们懂了异常处理。&lt;/p&gt;

&lt;p&gt;写出了监听 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.UnhandledException&lt;/code&gt; 事件的开发者，微软会认为他们已经学会了如何在 UI 线程中处理异常。于是允许开发者设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt; 来标记异常已被正确处理，程序可以不用退出了。&lt;/p&gt;

&lt;p&gt;还有一个事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.CurrentDomain.UnhandledException&lt;/code&gt;，然而这个事件却并不允许开发者标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt;。因为微软认为，应用程序域中所有的线程发生异常都会进入这个事件中，大多数开发者都不明白这些线程这些异常是怎么回事，所以不认为这些开发者具备正确处理这些异常的能力。比如 WPF 的触摸模块发生了异常，开发者知道如何恢复吗？并不知道，还不如结束掉程序然后重启呢！&lt;/p&gt;

&lt;p&gt;在这个事件中，有一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsTerminating&lt;/code&gt; 指示是否应用程序正因为这次异常准备退出，不过开发者并不能拿这个属性做些什么。&lt;/p&gt;

&lt;p&gt;但还是要照顾更高级的开发者的，于是祭出新的配置——&lt;code class=&quot;highlighter-rouge&quot;&gt;legacyUnhandledExceptionPolicy&lt;/code&gt;！&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 文件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;runtime&amp;gt;&lt;/code&gt; 节点中添加如下代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;legacyUnhandledExceptionPolicy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你找不到在 App.config 的哪个地方，我再用一段代码标注一下，大概在这里：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;startup useLegacyV2RuntimeActivationPolicy=&quot;true&quot;&amp;gt;
            &amp;lt;supportedRuntime version=&quot;v4.0&quot; sku=&quot;.NETFramework,Version=v4.8&quot; /&amp;gt;
        &amp;lt;/startup&amp;gt;
        &amp;lt;runtime&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;legacyUnhandledExceptionPolicy enabled=&quot;1&quot; /&amp;gt;
&lt;/span&gt;        &amp;lt;/runtime&amp;gt;
    &amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;加上了这个配置之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.CurrentDomain.UnhandledException&lt;/code&gt; 事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsTerminating&lt;/code&gt; 就变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 啦！也就是说，程序并不会因为这次的异常而崩溃退出。&lt;/p&gt;

&lt;p&gt;既然你通过这个配置节点于微软达成了契约，你就需要好好地在 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.CurrentDomain.UnhandledException&lt;/code&gt; 事件中写好异常的恢复逻辑。如果不好好恢复，小心有些致命的异常会导致你的程序出现雪崩式的错误，最终 Windows 还是会通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;CorruptedStateException&lt;/code&gt; 把你干掉的！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/186854/how-to-prevent-an-exception-in-a-background-thread-from-terminating-an-applicati&quot;&gt;c# - How to prevent an exception in a background thread from terminating an application? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/exceptions-in-managed-threads?wt.mc_id=MVP&quot;&gt;Exceptions in Managed Threads - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 28 Nov 2019 08:08:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/prevent-app-crash-by-background-thread.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/prevent-app-crash-by-background-thread.html</guid>
        
        <category>AppDomain</category>
        
        <category>Application</category>
        
        <category>Dispatcher</category>
        
        <category>legacyUnhandledExceptionPolicy</category>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>WPF 程序如何跨窗口/跨进程设置控件焦点</title>
        <description>&lt;p&gt;WPF 程序提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Focus&lt;/code&gt; 方法和 &lt;code class=&quot;highlighter-rouge&quot;&gt;TraversalRequest&lt;/code&gt; 来在 WPF 焦点范围内转移焦点。但如果 WPF 窗口中嵌入了其他框架的 UI（比如另一个子窗口），那么就需要使用其他的方法来设置焦点了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;一个粗略的设置方法是，使用 Win32 API：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;SetFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;传入的是要设置焦点的窗口的句柄。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/31570683/6233938&quot;&gt;winapi - Win32: C++: How do I re-focus on Parent Window after clicking in a child window? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 27 Nov 2019 00:53:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/move-focus-to-win32-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/move-focus-to-win32-window.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>PowerShell 的命令行启动参数（可用于执行命令、传参或进行环境配置）</title>
        <description>&lt;p&gt;有一些程序不支持被直接启动，而要求通过命令行启动。这个时候，你就需要使用 PowerShell 或者 PowerShell Core 来启动这样的程序。我们都知道如何在命令行或各种终端中启动一个程序，但是当你需要自动启动这个程序的时候，你就需要知道如何通过 PowerShell 或其他命令行终端来启动一个程序，而不是手工输入然后回车运行了。&lt;/p&gt;

&lt;p&gt;本文就介绍 PowerShell 的命令行启动参数。利用这些参数，你可以自动化地通过 PowerShell 程序来完成一些原本需要通过手工执行的操作或者突破一些限制。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一些必须通过命令行启动的程序&quot;&gt;一些必须通过命令行启动的程序&lt;/h2&gt;

&lt;p&gt;一般来说，编译生成的 exe 程序都可以直接启动，即便是命令行程序也是如此。但是有一些程序就是要做一些限制。比如下面的 FRP 反向代理程序：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-12-12-48-57.png&quot; alt=&quot;FRP 反向代理程序限制必须从命令行启动&quot; /&gt;&lt;/p&gt;

&lt;p&gt;借助 cmd.exe 来启动的方法可以参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/cmd-startup-arguments&quot;&gt;cmd.exe 的命令行启动参数（可用于执行命令、传参或进行环境配置） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么我们如何能够借助于 PowerShell 或者 PowerShell 来启动它呢？&lt;/p&gt;

&lt;h2 id=&quot;powershell-的帮助文档&quot;&gt;PowerShell 的帮助文档&lt;/h2&gt;

&lt;p&gt;先打开一个 PowerShell。&lt;/p&gt;

&lt;p&gt;对于 Windows 自带的基于 .NET Framework 的 PowerShell，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;powershell&lt;/code&gt; 命令可以直接启动 PowerShell。对于基于 .NET Core 版本的 PowerShell Core，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh&lt;/code&gt; 命令可以直接启动。&lt;/p&gt;

&lt;p&gt;关于 .NET Core 版本的 PowerShell Core 可以参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/install-and-run-powershell-core&quot;&gt;安装和运行 .NET Core 版本的 PowerShell - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接下来输入下面三个命令中的任何一个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PowerShell -Help&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PowerShell -?&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PowerShell /?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;或者对于 PowerShell Core 来说，是下面三个命令中的任何一个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh -Help&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh -?&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh /?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你就可以看到 PowerShell 的使用说明：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-12-14-55-11.png&quot; alt=&quot;PowerShell 的使用说明&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;powershell-的启动参数示例&quot;&gt;PowerShell 的启动参数示例&lt;/h2&gt;

&lt;h3 id=&quot;使用-powershell-间接启动一个程序并传入参数&quot;&gt;使用 PowerShell 间接启动一个程序并传入参数&lt;/h3&gt;

&lt;p&gt;下面的命令，使用 PowerShell 间接启动 frpc.exe 反向代理程序，并给 frpc.exe 程序传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;-c ./frpc.ini&lt;/code&gt; 的启动参数：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv\frpc.exe -c ./frpc.ini&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者简写为：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv\frpc.exe -c ./frpc.ini&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上使用 PowerShell 来做这些事情简直是用牛刀杀鸡，因为本身 PowerShell 非常强大。我们只是因为一些程序的限制不得不使用这样的方案来启动程序而已。&lt;/p&gt;

&lt;p&gt;比如其中之一，执行脚本。&lt;/p&gt;

&lt;h3 id=&quot;使用-powershell-执行命令脚本后保留窗口不退出&quot;&gt;使用 PowerShell 执行命令/脚本后保留窗口不退出&lt;/h3&gt;

&lt;p&gt;需要加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;-NoExit&lt;/code&gt; 参数。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoExit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv\frpc.exe -c ./frpc.ini&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一定要注意，&lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; 和后面的命令必须放到最末尾，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; 后面的所有字符串都会被解析为需要执行的命令。&lt;/p&gt;

&lt;h3 id=&quot;使用-powershell-执行多条命令脚本&quot;&gt;使用 PowerShell 执行多条命令/脚本&lt;/h3&gt;

&lt;p&gt;多条脚本之间使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 作为分隔：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv\frpc.exe -c ./frpc.ini&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv\frps.exe -c ./frps.ini&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果引号里面还需要写引号，则可以把里面的引号改成单引号 &lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt; 或者把外面的引号改为单引号 &lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;使用-powershell-间接执行一个脚本&quot;&gt;使用 PowerShell 间接执行一个脚本&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Execute a PowerShell Command in a session&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PowerShell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Get-EventLog -LogName security&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Run a script block in a session&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PowerShell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-EventLog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-LogName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# An alternate way to run a command in a new session&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PowerShell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;amp; {Get-EventLog -LogName security}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;附-powershell-的全部启动参数说明&quot;&gt;附 PowerShell 的全部启动参数说明&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PowerShell[.exe] [-PSConsoleFile &amp;lt;文件&amp;gt; | -Version &amp;lt;版本&amp;gt;]
    [-NoLogo] [-NoExit] [-Sta] [-Mta] [-NoProfile] [-NonInteractive]
    [-InputFormat {Text | XML}] [-OutputFormat {Text | XML}]
    [-WindowStyle &amp;lt;样式&amp;gt;] [-EncodedCommand &amp;lt;Base64 编码命令&amp;gt;]
    [-ConfigurationName &amp;lt;字符串&amp;gt;]
    [-File &amp;lt;文件路径&amp;gt; &amp;lt;参数&amp;gt;] [-ExecutionPolicy &amp;lt;执行策略&amp;gt;]
    [-Command { - | &amp;lt;脚本块&amp;gt; [-args &amp;lt;参数数组&amp;gt;]
                  | &amp;lt;字符串&amp;gt; [&amp;lt;命令参数&amp;gt;] } ]

PowerShell[.exe] -Help | -? | /?

-PSConsoleFile
    加载指定的 Windows PowerShell 控制台文件。若要创建控制台
    文件，请在 Windows PowerShell 中使用 Export-Console。

-Version
    启动指定版本的 Windows PowerShell。
    使用参数输入版本号，如 &quot;-version 2.0&quot;。

-NoLogo
    启动时隐藏版权标志。

-NoExit
    运行启动命令后不退出。

-Sta
    使用单线程单元启动 shell。
    单线程单元(STA)是默认值。

-Mta
    使用多线程单元启动 shell。

-NoProfile
    不加载 Windows PowerShell 配置文件。

-NonInteractive
    不向用户显示交互式提示。

-InputFormat
    描述发送到 Windows PowerShell 的数据的格式。有效值为
    &quot;Text&quot; (文本字符串)或 &quot;XML&quot; (序列化的 CLIXML 格式)。

-OutputFormat
    确定如何设置 Windows PowerShell 输出内容的格式。有效值
    为 &quot;Text&quot; (文本字符串)或 &quot;XML&quot; (序列化的 CLIXML 格式)。

-WindowStyle
    将窗口样式设置为 Normal、Minimized、Maximized 或 Hidden。

-EncodedCommand
    接受 base-64 编码字符串版本的命令。使用此参数
    向 Windows PowerShell 提交需要复杂引号
    或大括号的命令。

-ConfigurationName
    指定运行 Windows PowerShell 的配置终结点。
    该终结点可以是在本地计算机上注册的任何终结点，包括
    默认的 Windows PowerShell 远程处理终结点或具有特定用户角色功能
    的自定义终结点。

-File
    在本地作用域(&quot;dot-sourced&quot;)中运行指定的脚本，以便
    脚本创建的函数和变量可以在当前
    会话中使用。输入脚本文件路径和任何参数。
    File 必须是命令中的最后一个参数，因为在 File 参数
    名称后面键入的所有字符都将解释
    为后跟脚本参数的脚本文件路径。

-ExecutionPolicy
    设置当前会话的默认执行策略，并将其保存
    在 $env:PSExecutionPolicyPreference 环境变量中。
    该参数不会更改在注册表中
    设置的 Windows PowerShell 执行策略。

-Command
    执行指定的命令(和任何参数)，就好像它们是
    在 Windows PowerShell 命令提示符下键入的一样，然后退出，除非
    指定了 NoExit。Command 的值可以为 &quot;-&quot;、字符串或
    脚本块。

    如果 Command 的值为 &quot;-&quot;，则从标准输入中读取
    命令文本。

    如果 Command 的值为脚本块，则脚本块必须
    用大括号({})括起来。只有在 Windows PowerShell 中运行 PowerShell.exe 时，
    才能指定脚本块。脚本块的结果将作为反序列化的 XML 对象
    (而非活动对象)返回到父 Shell。

    如果 Command 的值为字符串，则 Command 必须是命令中的
    最后一个参数，因为在命令后面键入的所有字符
    都将解释为命令参数。

    若要编写运行 Windows PowerShell 命令的字符串，请使用以下格式:
        &quot;&amp;amp; {&amp;lt;命令&amp;gt;}&quot;
    其中，引号表示一个字符串，调用运算符(&amp;amp;)
    导致执行命令。

-Help, -?, /?
    显示此消息。如果在 Windows PowerShell 中键入 PowerShell.exe
    命令，请在命令参数前面添加连字符(-)，而不是添加正
    斜杠(/)。你可以在 Cmd.exe 中使用连字符或正斜杠。

示例
    PowerShell -PSConsoleFile SqlSnapIn.Psc1
    PowerShell -version 2.0 -NoLogo -InputFormat text -OutputFormat XML
    PowerShell -ConfigurationName AdminRoles
    PowerShell -Command {Get-EventLog -LogName security}
    PowerShell -Command &quot;&amp;amp; {Get-EventLog -LogName security}&quot;

    # To use the -EncodedCommand parameter:
    $command = 'dir &quot;c:\program files&quot; '
    $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
    $encodedCommand = [Convert]::ToBase64String($bytes)
    powershell.exe -encodedCommand $encodedCommand
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/questions/612409/how-do-i-run-multiple-commands-on-one-line-in-powershell&quot;&gt;windows - How do I run multiple commands on one line in PowerShell? - Super User&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/2608144/6233938&quot;&gt;How to split long commands over multiple lines in PowerShell - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/9362722/6233938&quot;&gt;Stop Powershell from exiting - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.pstips.net/powershell-any-key-exit.html&quot;&gt;Powershell 任意键退出 – PowerShell 中文博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 20 Nov 2019 06:39:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/powershell-startup-arguments.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/powershell-startup-arguments.html</guid>
        
        
        <category>windows</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>清理 git 仓库太繁琐？试试 bfg！删除敏感信息删除大文件一句命令搞定（比官方文档还详细的使用说明）</title>
        <description>&lt;p&gt;你可能接触过 &lt;code class=&quot;highlighter-rouge&quot;&gt;git-filter-branch&lt;/code&gt; 来清理 git 仓库，不过同时也能体会到这个命令使用的繁琐，以及其超长的执行时间。&lt;/p&gt;

&lt;p&gt;现在，你可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;bfg&lt;/code&gt; 来解决问题了！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-bfg&quot;&gt;安装 bfg&lt;/h2&gt;

&lt;h3 id=&quot;传统方式安装不推荐&quot;&gt;传统方式安装（不推荐）&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;下载安装 &lt;a href=&quot;https://www.java.com/zh_CN/download/&quot;&gt;Java 运行时&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;下载安装 &lt;a href=&quot;https://search.maven.org/classic/remote_content?g=com.madgag&amp;amp;a=bfg&amp;amp;v=LATEST&quot;&gt;bfg.jar&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里并不推荐使用传统方式安装，因为传统方式安装后，&lt;code class=&quot;highlighter-rouge&quot;&gt;bfg&lt;/code&gt; 不会成为你计算机的命令。在实际使用工具的时候，你必须为你的每一句命令加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;java -jar bfg.jar&lt;/code&gt; 前缀来使用 Java 运行时间接运行。&lt;/p&gt;

&lt;h3 id=&quot;使用包管理器-scoop-安装&quot;&gt;使用包管理器 scoop 安装&lt;/h3&gt;

&lt;p&gt;如果你使用包管理器 scoop，那么安装将会非常简单，只需要以下几个命令。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;scoop install bfg&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;scoop bucket add java&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;scoop install java/openjdk&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安装 bfg：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Installing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'bfg'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.13.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg-1.13.0.jar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.8&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;============================================================================================================================&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Checking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bfg-1.13.0.jar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Linking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;~\scoop\apps\bfg\current&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;~\scoop\apps\bfg\1.13.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Creating&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;shim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'bfg'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'bfg'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.13.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;was&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;installed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;successfully&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'bfg'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;suggests&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;installing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'java/oraclejdk'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'java/openjdk'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装 Java 源：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Checking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;repo...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;was&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;successfully.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装 Jdk：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;java/openjdk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Installing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'openjdk'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;13.0.1&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;openjdk-13.0.1_windows-x64_bin.zip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;186.9&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=======================================================================================================&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Checking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;openjdk-13.0.1_windows-x64_bin.zip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Extracting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;openjdk-13.0.1_windows-x64_bin.zip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Linking&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;~\scoop\apps\openjdk\current&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;~\scoop\apps\openjdk\13.0.1-9&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'openjdk'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;13.0.1&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;was&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;installed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;successfully&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;当你准备好清理你的仓库的时候，需要进行一些准备。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;克隆一个镜像仓库（&lt;code class=&quot;highlighter-rouge&quot;&gt;git clone&lt;/code&gt; 命令加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;--mirror&lt;/code&gt; 参数）
    &lt;ul&gt;
      &lt;li&gt;这样，当你 &lt;code class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt; 的时候，会更新远端仓库的所有引用&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cd&lt;/code&gt; 到你要清理的仓库路径的根目录
    &lt;ul&gt;
      &lt;li&gt;如果你没有前往根目录，那么本文后面的所有命令的最后面你都应该加上路径&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;可能需要解除保护
    &lt;ul&gt;
      &lt;li&gt;如果本文后面的命令你遇到了受保护的提交，那么需要在所有命令的后面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;--no-blob-protection&lt;/code&gt; 参数&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;常见用法&quot;&gt;常见用法&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;bfg&lt;/code&gt; 来清理仓库比 git 原生的 &lt;code class=&quot;highlighter-rouge&quot;&gt;git-filter-branch&lt;/code&gt; 快得多。官方说法是，10-720 倍：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;turning an overnight job into one that takes less than ten minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;将一整夜的工作缩减到不到十分钟。&lt;/p&gt;

&lt;h3 id=&quot;删除误上传的大文件&quot;&gt;删除误上传的大文件&lt;/h3&gt;

&lt;p&gt;使用下面的命令，可以将仓库历史中大于 500M 的文件都删除掉。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-blobs-bigger-than&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;500M&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;删除特定的一个或多个文件&quot;&gt;删除特定的一个或多个文件&lt;/h3&gt;

&lt;p&gt;删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.snk&lt;/code&gt; 文件：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv.snk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;删除 walterlv.snk 或 lindexi.snk 文件：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lindexi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;snk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如原来仓库结构是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- README.md
- Security.md
- walterlv.snk
+ test
    - lindexi.snk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么删除完后，根目录的 walterlv.snk 和 test 子目录下的 lindexi.snk 就都删除了。&lt;/p&gt;

&lt;h3 id=&quot;删除文件夹&quot;&gt;删除文件夹&lt;/h3&gt;

&lt;p&gt;删除名字为 walterlv 的文件夹：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-folders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此命令可以与上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;--delete-files&lt;/code&gt; 放在一起执行：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-folders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv.snk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;删除敏感的密码信息&quot;&gt;删除敏感的密码信息&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--replace-text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expression-file.txt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，这里的 expression-file.txt 名称是随便取的，你可以取其他任何名称，只要在命令里输入正确的名称（可能需要包含路径）就行。&lt;/p&gt;

&lt;p&gt;但是 expression-file.txt 里面的内容却是我们需要关注的重点。&lt;/p&gt;

&lt;p&gt;此文件中的每一行是一个匹配表达式。默认情况下，每一个表达式被视为一段文本常量，但你可以通过指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;regex:&lt;/code&gt; 前缀来说明此表达式是一个正则表达式，或者指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;glob:&lt;/code&gt; 前缀。每一个表达式的后面可以加上 ‘==&amp;gt;’ 来指定匹配的文件应该被替换成什么（如果没有指定，就会被替换成默认值 &lt;code class=&quot;highlighter-rouge&quot;&gt;***REMOVED***&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;下面这个例子示例将 git 仓库中所有文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;密码：123456&lt;/code&gt; 字符串替换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;***REMOVED***&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;密码：123456
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更复杂一点的，下面的例子示例将 git 仓库中所有文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;密码：123456&lt;/code&gt; 字符串替换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;密码：******&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;密码：123456 ==&amp;gt; 密码：******
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还可以使用正则表达式：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;regex:密码：\d+ ==&amp;gt; 密码：******
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;推回远端仓库&quot;&gt;推回远端仓库&lt;/h2&gt;

&lt;p&gt;当你在本地操作完镜像仓库之后，可以将其推回原来的远端仓库了。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后，有一个不必要的操作。就是回收已经没有引用的旧提交，这可以减小本地仓库的大小：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reflog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expire&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--expire&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--prune&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--aggressive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;附命令行用法输出&quot;&gt;附命令行用法输出&lt;/h2&gt;

&lt;p&gt;直接在命令行输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;bfg&lt;/code&gt; 可以看 &lt;code class=&quot;highlighter-rouge&quot;&gt;bfg&lt;/code&gt; 命令行的用法。我贴在下面可以让还没安装的小伙伴感受一下它的功能：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\BfgDemoRepo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1.13.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Usage:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-blobs-bigger-than&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;than&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'128K'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'1M'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;etc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-biggest-blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NUM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NUM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;biggest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-bi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-blobs-with-ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blob-ids-file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.class'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.{txt,log}'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;within&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-folders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;folders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'.svn'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*-tmp'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;within&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--convert-to-git-lfs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.zip'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.mp4'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LFS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-rt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--replace-text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expressions-file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replacing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matched&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expressions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;be&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;per&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;treated&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;but&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'regex:'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'glob:'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prefixes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;are&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;supported&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'==&amp;gt;'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specify&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;than&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'***REMOVED***'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-fi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--filter-content-including&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file-content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filtering&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.{txt,properties}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--filter-content-excluding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;don&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'t do file-content filtering on files that match the specified expression (eg '&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;')
  -fs, --filter-content-size-threshold &amp;lt;size&amp;gt;
                           only do file-content filtering on files smaller than &amp;lt;size&amp;gt; (default is 1048576 bytes)
  -p, --protect-blobs-from &amp;lt;refs&amp;gt;
                           protect blobs that appear in the most recent versions of the specified refs (default is '&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HEAD&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;')
  --no-blob-protection     allow the BFG to modify even your *latest* commit. Not recommended: you should have already ensured your latest commit is clean.
  --private                treat this repo-rewrite as removing private data (for example: omit old commit ids from commit messages)
  --massive-non-file-objects-sized-up-to &amp;lt;size&amp;gt;
                           increase memory usage to handle over-size Commits, Tags, and Trees that are up to X in size (eg '&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10M&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;')
  &amp;lt;repo&amp;gt;                   file path for Git repository to clean
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我觉得你可能需要中文版，于是自己翻译了一下：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\BfgDemoRepo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1.13.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用法&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-blobs-bigger-than&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;移除大于&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;大小的文件（&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可填写诸如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'128K'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;、&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'1M'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;）&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-biggest-blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NUM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;从大到小移除&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NUM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;数量的文件&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-bi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--strip-blobs-with-ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blob-ids-file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;移除具有指定&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;对象&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的文件&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;移除具有指定名称的文件（例如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.class'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;、&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.{txt,log}'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;，仅匹配文件名而不能匹配路径）&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--delete-folders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;移除具有指定名称的文件夹（例如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'.svn'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;、&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*-tmp'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;，仅匹配文件夹名而不能匹配路径）&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--convert-to-git-lfs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;将指定名称的文件（例如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.zip'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;或&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.mp4'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;）解压到&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LFS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-rt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--replace-text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expressions-file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;查找文件内容，并替换其中匹配的文本。&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expressions-file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;是一个包含一个或多个匹配表达式的文件，文件中每一行是一个匹配表达式。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;默认情况下，每一个表达式被视为一段文本常量，但你可以通过指定&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'regex:'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;前缀来说明此表达式是一个正则表达式，或者指定&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'glob:'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;前缀。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;每一个表达式的后面可以加上&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'==&amp;gt;'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;来指定匹配的文件应该被替换成什么（如果没有指定，就会被替换成默认值&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'***REMOVED***'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-fi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--filter-content-including&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定文件名（例如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.{txt,properties}'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;），在进行内容替换的时候只对这些文件进行处理。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--filter-content-excluding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定文件名（例如&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*.{xml,pdf}'&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;），在进行内容替换的时候不对这些文件进行处理。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--filter-content-size-threshold&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;仅对小于&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定的大小的文件替换内容。（默认值为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1048576&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;字节）&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--protect-blobs-from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;protect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;blobs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appear&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;most&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;recent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'HEAD'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--no-blob-protection&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;allow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BFG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;modify&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;even&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;latest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commit.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;recommended:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;you&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;have&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;already&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ensured&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;latest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clean.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--private&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;仅将本次操作视为个人数据的修改（这样生成的新提交会使用旧提交的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;，其他人拉取仓库的时候因为这些&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已经存在于是不会更新，以至于此更改实际上只影响自己）。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--massive-non-file-objects-sized-up-to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;increase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;over-size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Commits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Trees&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;are&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'10M'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rtyley.github.io/bfg-repo-cleaner/&quot;&gt;BFG Repo-Cleaner by rtyley&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Nov 2019 03:22:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/clean-up-git-repo-using-bfg.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/clean-up-git-repo-using-bfg.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>解决关闭模态窗口后，父窗口居然失去焦点跑到了其他窗口的后面的问题</title>
        <description>&lt;p&gt;显示一个模态窗口，正常而普遍的操作。然而却一直有一个难缠的 BUG：当关闭模态窗口时，父窗口有时会跑到其他程序窗口的后面！&lt;/p&gt;

&lt;p&gt;而最近读到了微软工程师写过的话之后，明白了这个 BUG 的产生缘由以及解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;这是什么-bug&quot;&gt;这是什么 BUG？&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-28-37.png&quot; alt=&quot;弹出模态窗口&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;弹出一个模态窗口，然后将模态窗口的父窗口设置为自身窗口；&lt;/li&gt;
  &lt;li&gt;切换到其他程序窗口中（比如 Windows 资源管理器窗口）；&lt;/li&gt;
  &lt;li&gt;切换回此模态窗口，然后关闭这个模态窗口上。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你会发现，模态窗口关闭后，父窗口并没有回到当前的顶层显示中。取而代之的，是其他程序的窗口（比如 Windows 资源管理器窗口）。
用一张图来描述这个 BUG，将是这样的：&lt;/p&gt;

&lt;p&gt;有这两个窗口，其中右边那个是我们开发的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-38-32.png&quot; alt=&quot;两个窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们的窗口在资源管理器上面。然后，我们弹出模态子窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-40-18.png&quot; alt=&quot;我们在上面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们操作一下资源管理器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-46-41.png&quot; alt=&quot;操作资源管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，回到模态子窗口中，把它关掉：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-43-35.png&quot; alt=&quot;关掉模态子窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们期待模态子窗口关掉后，它的父窗口会在顶层继续供我们操作，但实际上，Windows 资源管理器却成为了顶层，我们的程序“掉下去了”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-05-13-48-18.png&quot; alt=&quot;不符合预期的结果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解释和解决方法&quot;&gt;解释和解决方法&lt;/h2&gt;

&lt;p&gt;在《Windows 进化启示录》书中，微软有说到：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;当销毁模态对话框时，这个对话框刚好是拥有前台焦点的窗口。现在，窗口管理器需要找到其他的窗口并把前台焦点交给这个窗口。
窗口管理器会首先试着把前台焦点交给对话框的所有者窗口，但此时这个窗口却仍然是禁止的，因此窗口管理器将跳过所有者窗口，并继续查找没有被禁止的窗口。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这很明显是 Windows 的 BUG，然而让微软感到无奈的是，经常有程序喜欢依赖于微软的 BUG 进行开发，一旦微软修复了 BUG，那些依赖于 BUG 开发的程序将变得不正常！&lt;/p&gt;

&lt;p&gt;为解决兼容性问题的微软工程师默哀一分钟……&lt;/p&gt;

&lt;p&gt;我曾经尝试在模态子窗口关闭后激活一下父窗口，但这样会导致窗口的层级闪烁一下（Windows 资源管理器会短暂地显示到我们的窗口之上）。&lt;/p&gt;

&lt;p&gt;而这本书作者推荐的方法是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;重新激活所有者窗口&lt;/li&gt;
    &lt;li&gt;销毁模态对话框&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，我试着监听模态子窗口的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Closing&lt;/code&gt; 事件，在其中写下主窗口的激活调用，自此 BUG 才算解决。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChildModalWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Closing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Activate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将这样的解决办法封装成附加属性给所有的模态子窗口，这样设置附加属性即可解决问题。或者统一模态子窗口的窗口样式，在样式中解决这个 BUG，这样，所有使用了此窗口样式的模态子窗口也将解决问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;《伟大的产品：Windows 进化启示录》by 微软软件工程师 Raymond Chen (2016 年 3 月第 1 版，第 112 页)&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Nov 2019 01:58:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-owner-window-dropping-down-when-close-a-modal-child-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-owner-window-dropping-down-when-close-a-modal-child-window.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>C#/.NET 当我们在写事件 += 和 -= 的时候，方法是如何转换成事件处理器的</title>
        <description>&lt;p&gt;当我们在写 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 事件的时候，我们会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的右边写上事件处理函数。我们可以写很多种不同的事件处理函数的形式，那么这些形式都是一样的吗？如果你不注意，可能出现内存泄漏问题。&lt;/p&gt;

&lt;p&gt;本文将讲解事件处理函数的不同形式，理解了这些可以避免编写代码的时候出现内存相关的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;典型的事件处理函数&quot;&gt;典型的事件处理函数&lt;/h2&gt;

&lt;p&gt;事件处理函数本质上是一个委托，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 事件是这样定义的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 这是简化的代码。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemEventHandler&lt;/code&gt; 是一个委托类型：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个典型的事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 会像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的右边传入的是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的委托实例。&lt;/p&gt;

&lt;h2 id=&quot;变种事件处理函数&quot;&gt;变种事件处理函数&lt;/h2&gt;

&lt;p&gt;除了上面直接创建的目标类型的委托之外，还有其他类型可以放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的右边：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 方法组。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Lambda 表达式。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Lambda 表达式。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 事件引发时，代码会在这里执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 匿名方法。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 事件引发时，代码会在这里执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 委托类型的局部变量（或者字段）。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 局部方法（或者局部静态方法）。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为我们可以通过编写事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 方法来观察事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 是什么类型的什么实例，所以可以很容易验证以上每一种实例最终被加入到事件中的真实实例。&lt;/p&gt;

&lt;p&gt;实际上我们发现，无论哪一个，最终传入的都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemEventHandler&lt;/code&gt; 类型的实例。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-28-17-03-49.png&quot; alt=&quot;都是同一类型的实例&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而我们知道，只有直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的那个和局部变量那个真正是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemEventHandler&lt;/code&gt; 类型的实例，其他都不是。&lt;/p&gt;

&lt;p&gt;那么中间发生了什么样的转换使得我们所有种类的写法最终都可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 呢？&lt;/p&gt;

&lt;h2 id=&quot;编译器类型转换&quot;&gt;编译器类型转换&lt;/h2&gt;

&lt;p&gt;具有相同签名的不同委托类型，彼此之前并没有继承关系，因此在运行时是不可以进行类型转换的。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onChanged1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onChanged2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged1&lt;/code&gt; 的实例不可以赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged2&lt;/code&gt;，反过来 &lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged2&lt;/code&gt; 的实例也不可以赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged1&lt;/code&gt;。于是这里只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged1&lt;/code&gt; 才可以作为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的右边，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;onChanged2&lt;/code&gt; 放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 右边是会出现编译错误的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-28-17-16-38.png&quot; alt=&quot;不能转换&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，我们可以放 Lambda 表达式，可以放匿名函数，可以放方法组，也可以放局部函数。因为这些类型可以在编译期间，由编译器帮助进行类型转换。而转换的效果就类似于我们自己编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;new FileSystemEventHandler(xxx)&lt;/code&gt; 一样。&lt;/p&gt;

&lt;h2 id=&quot;不是同一个委托实例&quot;&gt;不是同一个委托实例&lt;/h2&gt;

&lt;p&gt;看下面这一段代码，你认为可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 成功吗？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上这是可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 成功的。&lt;/p&gt;

&lt;p&gt;我们平时编写代码的时候，下面的情况可能会多一些，于是自然而然以为 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 可以成功，因为他们“看起来”是同一个实例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在读完刚刚那一段之后，我们就可以知道，实际上这一段和上面 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来委托的写法在运行时是一模一样的。&lt;/p&gt;

&lt;p&gt;如果你想测试，那么在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的时候为对象加上一个 Id，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候你就会发现这是一个新对象（因为没有 Id）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-28-17-32-48.png&quot; alt=&quot;不是同一个对象&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，你平时众多的编码经验会告诉你，这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 是一定可以成功的。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 时传入的委托实例即便不是同一个，也是可以成功 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的。&lt;/p&gt;

&lt;h2 id=&quot;---是怎么做的&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 是怎么做的&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 到底是怎么做的，可以在不同实例时也能 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 成功呢？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 实际上是调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 方法，并生成一个新的委托实例赋值给 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的左边。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;onChangedHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onChangedHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;onChangedHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onChangedHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而最终的判断也是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt; 方法来比较委托的实例是否相等的（&lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt; 也是调用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt;）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InternalEqualTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// do an optimistic check first. This is hopefully cheap enough to be worth&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_methodPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// even though the fields were not all equals the delegates may still match&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// When target carries the delegate itself the 2 targets (delegates) may be different instances&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// but the delegates are logically the same&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// if that's the case the delegates may still be equals but we need to make a more complicated check&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// different delegate kind&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// they are both closed over the first arg&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// fall through method handle check&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// different delegate kind&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Ignore the target as it will be the delegate instance, though it may be a different one&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
        if (_methodPtr != d._methodPtr)
            return false;
            */&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodPtrAux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// fall through method handle check&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// method ptrs don't match, go down long path&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InternalEqualMethodHandles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_methodBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是可以看出来，判断相等就是两个关键对象的判断相等：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;方法所在的对象&lt;/li&gt;
  &lt;li&gt;方法信息（对应到反射里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;MethodInfo&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;继续回到这段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的对象就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;，方法信息就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnChanged&lt;/code&gt; 的信息，也就是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// this 就是对象，OnChanged 就是方法信息。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;于是什么样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 才可以把 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 加进去的事件处理函数减掉呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;必须是同一个对象的同一个方法&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用方法组、静态局部函数、委托字段的方式创建的委托实例，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候无视哪个委托实例，都是可以减掉的；&lt;/li&gt;
  &lt;li&gt;使用局部函数、委托变量，在同一个上下文中，是可以减掉的，如果调用是再次进入此函数，则不能减掉（因为委托方法所在的对象实例不同）&lt;/li&gt;
  &lt;li&gt;使用 Lambda 表达式、匿名函数是不能减掉的，因为每次编写的 Lambda 表达式和匿名函数都会创建新的包含此对象的实例。&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Tue, 29 Oct 2019 03:53:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/method-group-and-event-handler.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/method-group-and-event-handler.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何在 .NET 项目中开启不安全代码（以便启用 unsafe fixed 等关键字）</title>
        <description>&lt;p&gt;有小伙伴希望在 .NET 代码中使用指针，操作非托管资源，于是可能使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;fixed&lt;/code&gt; 关键字。但使用此关键字的前提是需要在项目中开启不安全代码。&lt;/p&gt;

&lt;p&gt;本文介绍如何在项目中开启不安全代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;入门方法&quot;&gt;入门方法&lt;/h2&gt;

&lt;p&gt;第一步：在你需要启用不安全代码的项目上点击右键，然后选择属性：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-15-34-33.png&quot; alt=&quot;项目 - 属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二步：在“生成”标签下，勾选上“允许不安全代码”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-15-36-34.png&quot; alt=&quot;允许不安全代码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第三步：切换到 Release 配置，再勾上一次“允许不安全代码”（确保 Debug 和 Release 都打开）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-15-38-36.png&quot; alt=&quot;在 Release 允许不安全代码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;方法结束。&lt;/p&gt;

&lt;p&gt;如果你一开始选择了“所有配置”，那么就不需要分别在 Debug 和 Release 下打开了，一次打开即可。&lt;/p&gt;

&lt;h2 id=&quot;高级方法&quot;&gt;高级方法&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;推荐&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果你使用 .NET Core / .NET Standard 项目，那么你可以修改项目文件来实现，这样项目文件会更加清真。&lt;/p&gt;

&lt;p&gt;第一步：在你需要启用不安全代码的项目上点击右键，然后选择编辑项目文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-15-42-42.png&quot; alt=&quot;编辑项目文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二步：在你的项目文件的属性组中添加一行 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;我已经把需要新增的行高亮出来了&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;临时方法&quot;&gt;临时方法&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;不推荐&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果你只是临时希望加上不安全代码开关，则可以在编译的时候加入 &lt;code class=&quot;highlighter-rouge&quot;&gt;-unsafe&lt;/code&gt; 命令行参数：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;csc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-unsafe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，不能给 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;-unsafe&lt;/code&gt; 参数来编译项目，只能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;csc&lt;/code&gt; 加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;-unsafe&lt;/code&gt; 来编译文件。因此使用场景非常受限，不推荐使用。&lt;/p&gt;

&lt;h2 id=&quot;其他说明&quot;&gt;其他说明&lt;/h2&gt;

&lt;p&gt;第一种方法（入门方法）和第二种方法（高级方法）最终的修改是有一些区别的。入门方法会使得项目文件中有针对于 Debug 和 Release 的不同配置，代码会显得冗余；而高级方法中只增加了一行，对任何配置均生效。&lt;/p&gt;

&lt;p&gt;因此如果可能，尽量使用高级方法呗。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;

--    &amp;lt;PropertyGroup Condition=&quot;'$(Configuration)|$(Platform)'=='Debug|AnyCPU'&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
--    &amp;lt;/PropertyGroup&amp;gt;
&lt;/span&gt;
--    &amp;lt;PropertyGroup Condition=&quot;'$(Configuration)|$(Platform)'=='Release|AnyCPU'&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--      &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
--    &amp;lt;/PropertyGroup&amp;gt;
&lt;/span&gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;即使是 .NET Framework 也是可以使用 SDK 风格的项目文件的，详情请阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 29 Oct 2019 03:53:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/allow-unsafe-code-in-dotnet-project.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/allow-unsafe-code-in-dotnet-project.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>不要在 C# 代码中写部分命名空间（要么不写，要么写全），否则会有源码兼容性问题</title>
        <description>&lt;p&gt;我只是增加库的一个 API，比如增加几个类而已，应该不会造成兼容性问题吧。对于编译好的二进制文件来说，不会造成兼容性问题；但——可能造成源码不兼容。&lt;/p&gt;

&lt;p&gt;本文介绍可能的源码不兼容问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/source-code-compatibility-issue-of-adding-apis.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/source-code-compatibility-issue-of-adding-apis-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;比如我有一个项目 P 引用 A 和 B 两个库。其中使用到了 A 库中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.A.Diagnostics.Foo&lt;/code&gt; 类型。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们在 B 库中新增一个类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.B.Diagnostics.Bar&lt;/code&gt; 类型。&lt;/p&gt;

&lt;p&gt;那么上面的代码将无法完成编译，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnosis&lt;/code&gt; 命名空间将具有不确定的含义，其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型也将无法在不确定的命名空间中找到。&lt;/p&gt;

&lt;p&gt;因此：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;强烈建议遵守&lt;/strong&gt; 使用类型的时候，要么不写命名空间（完全留给 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt;），要么写全命名空间（从第一段开始写，不要省略任何部分），否则就容易与其他命名空间冲突；&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;可选遵守&lt;/strong&gt; 在库中新增 API 的时候，可能需要考虑避免将部分命名空间写成过于通用的名称。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;是的，即使是单纯的新增 API 也可能会导致使用库的一方在源码级不兼容。当然二进制还是兼容的。&lt;/p&gt;

&lt;p&gt;另外，&lt;a href=&quot;https://github.com/OpportunityLiu&quot;&gt;OpportunityLiu&lt;/a&gt; 提醒，如果命名空间是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.B.Walterlv.A.Diagnostics.Bar&lt;/code&gt;，一样可以让写全了的命名空间炸掉。呃……还是不要在库里面折腾这样的命名空间好……不然代码当中到处充斥着 &lt;code class=&quot;highlighter-rouge&quot;&gt;global::&lt;/code&gt; 可是非常难受的。&lt;/p&gt;
</description>
        <pubDate>Tue, 29 Oct 2019 00:51:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/source-code-compatibility-issue-of-adding-apis.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/source-code-compatibility-issue-of-adding-apis.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>.NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表</title>
        <description>&lt;p&gt;我们知道，32 位程序在读取注册表的时候，会自动将注册表的路径映射到 32 位路径下，即在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wow6432Node&lt;/code&gt; 子节点下。但是 64 位程序不会映射到 32 位路径下。那么 64 位程序如何读取到 32 位程序写入的注册表路径呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;wow6432node&quot;&gt;Wow6432Node&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-15-37-14.png&quot; alt=&quot;Wow6432Node&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于 32 位程序，读取注册表路径的时候，会读到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wow6432Node&lt;/code&gt; 节点下的项：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-15-36-50.png&quot; alt=&quot;32 位&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这张图读取的就是前面截图中的节点。&lt;/p&gt;

&lt;p&gt;那么怎样编译的程序是 32-bit 的程序呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-16-30-02.png&quot; alt=&quot;x86&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-16-30-07.png&quot; alt=&quot;AnyCPU 32-bit preferred&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于 64 位程序，读取的时候就不会有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wow6432Node&lt;/code&gt; 路径部分。由于我没有在那个路径放注册表项，所以会得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-16-48-23.png&quot; alt=&quot;null&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么怎样编译的程序是 64-bit 的程序呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-16-33-59.png&quot; alt=&quot;x64&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-22-16-33-43.png&quot; alt=&quot;AnyCPU&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;如何在-64-位程序中读取-32-位注册表路径&quot;&gt;如何在 64 位程序中读取 32 位注册表路径&lt;/h2&gt;

&lt;p&gt;前面我们的例子代码是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistryHive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LocalMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;SOFTWARE\Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，相同的代码，在 32 位和 64 位进程下得到的结果是不同的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;32 位进程在 32 位系统上，64 位进程在 64 位系统上，读取的路径会是传入的路径；&lt;/li&gt;
  &lt;li&gt;32 位进程在 64 位系统上，读取的路径会包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wow6432Node&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么如何在 64 位进程中读取 32 位注册表路径呢？&lt;/p&gt;

&lt;p&gt;方法是在打开注册表项的时候，传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;RegistryView.Registry32&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;RegistryKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OpenBaseKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistryView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Registry32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;walterlvwin32&quot;&gt;Walterlv.Win32&lt;/h2&gt;

&lt;p&gt;可以在我的 GitHub 仓库中查看完整的实现。当然，除了上面那句话，其他都不是关键代码，在哪里都可以找得到的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2040103/6233938&quot;&gt;c# - Reading the registry and Wow6432Node key - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 08:59:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/read-32bit-registry-from-x64-process.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题</title>
        <description>&lt;p&gt;因为 Win32 的窗口句柄是可以跨进程传递的，所以可以用来实现跨进程 UI。不过，本文不会谈论跨进程 UI 的具体实现，只会提及其实现中的一个重要缓解，使用子窗口的方式。&lt;/p&gt;

&lt;p&gt;你有可能在使用子窗口之后，发现拖拽改变窗口大小的时候，子窗口中的内容不断闪烁。如果你也遇到了这样的问题，那么正好可以阅读本文来解决。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;你可以看一下下面的这张动图，感受一下窗口的闪烁：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-01-window-flicker.gif&quot; alt=&quot;窗口闪烁&quot; /&gt;&lt;/p&gt;

&lt;p&gt;实际上在拖动窗口的时候，是一直都在闪的，只是每次闪烁都非常快，截取 gif 的时候截不到。&lt;/p&gt;

&lt;p&gt;如果你希望实际跑一跑项目看看，可以使用下面的代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/a88f81477756af2913349970ba2f0bbab01aaf88/Walterlv.Demo.HwndWrapping/Walterlv.Demo.HwndWrapping&quot;&gt;walterlv.demo/Walterlv.Demo.HwndWrapping/Walterlv.Demo.HwndWrapping at a88f81477756af2913349970ba2f0bbab01aaf88 · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我特地提取了一个提交下的代码，如果你要尝试，不能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支修复了闪烁的问题。&lt;/p&gt;

&lt;p&gt;后来使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateWindowEx&lt;/code&gt; 创建了一个纯 Win32 窗口，这种闪烁现象更容易被截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-02-08-16-22.png&quot; alt=&quot;Win32 窗口闪烁&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-02-window-flicker.gif&quot; alt=&quot;Win32 窗口闪烁 - 动图&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class HwndWrapper : HwndHost
    {
        protected override HandleRef BuildWindowCore(HandleRef hwndParent)
        {
            const int WS_CHILD = 0x40000000;
&lt;span class=&quot;gi&quot;&gt;++          const int WS_CLIPCHILDREN = 0x02000000;
&lt;/span&gt;            var owner = ((HwndSource)PresentationSource.FromVisual(this)).Handle;

            var parameters = new HwndSourceParameters(&quot;demo&quot;)
            {
                ParentWindow = owner,
&lt;span class=&quot;gd&quot;&gt;--              WindowStyle = (int)(WS_CHILD),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++              WindowStyle = (int)(WS_CHILD | WS_CLIPCHILDREN),
&lt;/span&gt;            };
            var source = new HwndSource(parameters);
            source.RootVisual = new ChildPage();
            return new HandleRef(this, source.Handle);
        }

        protected override void DestroyWindowCore(HandleRef hwnd)
        {
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;正在探索……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/6500336/6233938&quot;&gt;wpf - Custom dwm drawn window frame flickers on resizing if the window contains a HwndHost element - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/f2c6a2d9bbb2&quot;&gt;WPF多进程UI探索（Like Chrome） - 简书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/helloj2ee/archive/2009/05/29/1491822.html&quot;&gt;关于WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解（个人认为还是相当全面的） - helloj2ee - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 06:14:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/window-flickers-on-resizing-if-the-window-contains-a-hwndhost-element.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/window-flickers-on-resizing-if-the-window-contains-a-hwndhost-element.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>System.ComponentModel.Win32Exception (0x80004005): 无效的窗口句柄。</title>
        <description>&lt;p&gt;在 WPF 获取鼠标当前坐标的时候，可能会得到一个异常：&lt;code class=&quot;highlighter-rouge&quot;&gt;System.ComponentModel.Win32Exception:“无效的窗口句柄。”&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;本文解释此异常的原因和解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;异常&quot;&gt;异常&lt;/h2&gt;

&lt;p&gt;获取鼠标当前相对于元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;element&lt;/code&gt; 的坐标的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者，还有其他的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果在按下窗口关闭按钮的时候调用以上代码，则会引发异常：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.ComponentModel.Win32Exception (0x80004005): 无效的窗口句柄。
   at Point MS.Internal.PointUtil.ClientToScreen(Point pointClient, PresentationSource presentationSource)
   at Point System.Windows.Input.MouseDevice.GetScreenPositionFromSystem()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;将窗口上的点转换到控件上的点的方法是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Convert a point from &quot;client&quot; coordinate space of a window into&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     the coordinate space of the screen.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     SecurityCritical: This code causes eleveation to unmanaged code via call to GetWindowLong&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     SecurityTreatAsSafe: This data is ok to give out&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     validate all code paths that lead to this.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityTreatAsSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// For now we only know how to use HwndSource.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CriticalHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClient&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AdjustForRightToLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClientToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最关键的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsafeNativeMethods.ClientToScreen&lt;/code&gt;，此方法要求窗口句柄依然有效，然而此时窗口已经关闭，句柄已经销毁。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

</description>
        <pubDate>Tue, 22 Oct 2019 06:13:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win32exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win32exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用 Direct3D11 的 OpenSharedResource 方法渲染来自其他进程/设备的共享资源（SharedHandle）</title>
        <description>&lt;p&gt;如果你得到了一个来自于其他进程或者其他模块的 Direct3D11 的共享资源，即 SharedHandle 句柄，那么可以使用本文提到的方法将其转换成 Direct3D11 的设备和纹理，这样你可以进行后续的其他处理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;sharpdx&quot;&gt;SharpDX&lt;/h2&gt;

&lt;p&gt;本文的代码会使用到 &lt;a href=&quot;https://www.nuget.org/packages?q=Tags%3A%22SharpDX%22&quot;&gt;SharpDX&lt;/a&gt; 库，因此，你需要在你的项目当中安装这些 NuGet 包：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 基础，必装 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.D3DCompiler&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.DXGI&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Mathematics&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct3D11&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其他，可选 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct2D1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct3D9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sharedhandle&quot;&gt;SharedHandle&lt;/h2&gt;

&lt;p&gt;Direct3D 支持在不同的 Direct3D 设备之间共享资源。需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceOptionFlags&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Shared&lt;/code&gt; 的纹理才可以支持共享，当然这不是本文要说的重点。&lt;/p&gt;

&lt;p&gt;本文要说的是，如果你拿到了一个来自于其他模块的共享资源句柄的时候，你可以如何使用它。&lt;/p&gt;

&lt;p&gt;你的使用可能类似于这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAcceleratedPaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sharedHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Int32Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirtyRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 通过 sharedHandle 进行后续的处理。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;opensharedresource&quot;&gt;OpenSharedResource&lt;/h2&gt;

&lt;p&gt;DirectX 中用来表示 Direct3D11 的设备类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ID3D11Device&lt;/code&gt;，它有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;OpenSharedResource&lt;/code&gt; 方法可以用来打开来自于其他设备的共享资源。&lt;/p&gt;

&lt;p&gt;对应到 SharpDX 中，用来表示 Direct3D11 的设备的类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.Direct3D11.Device&lt;/code&gt;，其有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;OpenSharedResource&amp;lt;T&amp;gt;&lt;/code&gt; 方法来打开来自于其他设备的共享资源。&lt;/p&gt;

&lt;p&gt;我们必须要创建一个自己的 Direct3D11 设备，因为设备是不共享的，代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DriverType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hardware&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeviceCreationFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BgraSupport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenSharedResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sharedHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;后续操作&quot;&gt;后续操作&lt;/h2&gt;

&lt;p&gt;在得到此共享资源之后，我们可以获得更多关于此资源的描述，以及有限地使用此资源的方法。&lt;/p&gt;

&lt;h3 id=&quot;获取-texture2d&quot;&gt;获取 Texture2D&lt;/h3&gt;

&lt;p&gt;可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueryInterface&lt;/code&gt; 获取某个资源相关的 COM 对象的引用。我们拿到的共享资源是 2D 纹理的话，我们可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueryInterface&lt;/code&gt; 获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.Direct3D11.Texture2D&lt;/code&gt; COM 对象的引用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;获取-texture2ddescription&quot;&gt;获取 Texture2DDescription&lt;/h3&gt;

&lt;p&gt;可以从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Texture2D&lt;/code&gt; 的实例中获取到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Texture2DDescription&lt;/code&gt;，这是用来描述此 2D 纹理创建时的各种信息。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 在 DirectX 的传统代码中，通常使用 desc 来作为 Texture2DDescription 实例命名的后缀。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 不过 C# 代码通常不这么干，这是 C++ 代码的习惯。在这里这么写是为了在得到 C++ 搜索结果的时候可以与本文所述的 C# 代码对应起来。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;获取-surface&quot;&gt;获取 Surface&lt;/h3&gt;

&lt;p&gt;或者，我们可以获取到 2D 图面，用于做渲染、绘制等操作。当然，是否能真正进行这些操作取决于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Texture2DDescription&lt;/code&gt; 中是否允许相关的操作。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DXGI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在获取到 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.DXGI.Surface&lt;/code&gt; 的 COM 组件引用之后，可以在内存中映射位图用于调试，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/map-directx-surface-to-bitmap&quot;&gt;将 Direct3D11 在 GPU 中的纹理（Texture2D）导出到内存或导出成图片文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/43347246/6233938&quot;&gt;c++ - Direct3D11: Sharing a texture between devices: black texture - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-opensharedresource&quot;&gt;ID3D11Device::OpenSharedResource (d3d11.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/windows/hardware/bb174560(v=vs.110)&quot;&gt;IDXGIResource interface (Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nn-dxgi-idxgiresource&quot;&gt;IDXGIResource (dxgi.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(q_)&quot;&gt;IUnknown::QueryInterface(Q,) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 06:07:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/direct3d11-open-shared-resource.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/direct3d11-open-shared-resource.html</guid>
        
        
        <category>directx</category>
        
        <category>sharpdx</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>将 Direct3D11 在 GPU 中的纹理（Texture2D）导出到内存（Map）或导出成图片文件</title>
        <description>&lt;p&gt;Direct3D11 的使用通常不是应用程序唯一的部分，于是使用 Direct3D11 的代码如何与其他模块正确地组合在一起就是一个需要解决的问题。&lt;/p&gt;

&lt;p&gt;本文介绍将 Direct3D11 在 GPU 中绘制的纹理映射到内存中，这样我们可以直接观察到此纹理是否是正确的，而不用担心是否有其他模块影响了最终的渲染过程。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;sharpdx&quot;&gt;SharpDX&lt;/h2&gt;

&lt;p&gt;本文的代码会使用到 &lt;a href=&quot;https://www.nuget.org/packages?q=Tags%3A%22SharpDX%22&quot;&gt;SharpDX&lt;/a&gt; 库，因此，你需要在你的项目当中安装这些 NuGet 包：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 基础，必装 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.D3DCompiler&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.DXGI&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Mathematics&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct3D11&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其他，可选 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct2D1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharpDX.Direct3D9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;来自于-direct3d11-的渲染纹理&quot;&gt;来自于 Direct3D11 的渲染纹理&lt;/h2&gt;

&lt;p&gt;本文不会说如何创建或者获取来自 Direct3D11 的渲染纹理，不过如果你希望了解，可以：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;自己创建：&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8%E5%B0%81%E8%A3%85%E7%9A%84-SharpDx-%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 使用封装的 SharpDx 控件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;或者从其他进程/模块获取：&lt;a href=&quot;/post/direct3d11-open-shared-resource&quot;&gt;使用 Direct3D11 的 OpenSharedResource 方法渲染来自其他进程/设备的共享资源（SharedHandle）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文接下来的内容，是在你已经获得了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.Direct3D11.Resource&lt;/code&gt; 的引用，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.Direct3D11.Texture2D&lt;/code&gt; 的前提之下。当然，如果你获得了其中任何一个实例，可以通过 COM 组件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueryInterface&lt;/code&gt; 方法获得其他实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;关键代码sharpdxdxgisurfacemap&quot;&gt;关键代码（SharpDX.DXGI.Surface.Map）&lt;/h2&gt;

&lt;p&gt;要获得 GPU 中渲染的图片，我们必须要将其映射到内存中才行。而映射到内存中的核心代码是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.DXGI.Surface&lt;/code&gt; 对象的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Map&lt;/code&gt; 方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DXGI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DXGI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 在这里使用位图的像素数据，坐标为 (x, y)。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 得到此坐标下的像素指针：&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     var ptr = ((byte*)map.DataPointer) + y * map.Pitch;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 得到此像素的颜色值：&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     var b = *(ptr + 4 * x);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     var g = *(ptr + 4 * x + 1);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     var r = *(ptr + 4 * x + 2);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     var a = *(ptr + 4 * x + 3);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dataStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Unmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意以上代码使用了不安全代码（指针），你需要为你的项目开启不安全代码开关，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/allow-unsafe-code-in-dotnet-project&quot;&gt;如何在 .NET 项目中开启不安全代码（以便启用 unsafe fixed 等关键字）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;你可能需要拷贝资源&quot;&gt;你可能需要拷贝资源&lt;/h2&gt;

&lt;p&gt;实际上，在使用上面的代码时，你可能会遇到错误，错误出现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Map&lt;/code&gt; 方法的调用上，描述为“参数错误”。实际上真正检查这里的两个参数时并不能发现究竟是哪个参数出了问题。&lt;/p&gt;

&lt;p&gt;实际上出问题的参数是 &lt;code class=&quot;highlighter-rouge&quot;&gt;surface&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;p&gt;一段 GPU 中的纹理要能够被映射到内存，必须要具有 CPU 的访问权。而是否具有 CPU 访问权在创建纹理的时候就已经确定下来了。&lt;/p&gt;

&lt;p&gt;如果前面你得到的纹理是自己创建的，那么恭喜你，你只需要改一下创建纹理的参数就好了。给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Texture2DDescription&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CpuAccessFlags&lt;/code&gt; 属性加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;CpuAccessFlags.Read&lt;/code&gt; 标识。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;；&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，如果此纹理不是由你自己创建的，那么就需要拷贝一份新的纹理了。当然，拷贝过程发生在 GPU 中，占用的也是 GPU 专用内存（即显存，如果有的话）。&lt;/p&gt;

&lt;p&gt;拷贝需要做到两点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Texture2DDescription&lt;/code&gt;（一定要是新的实例，你不能影响原来的实例），然后修改其 CPU 访问权限为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Read&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ImmediateContext&lt;/code&gt; 实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyResource&lt;/code&gt; 方法来拷贝资源（此实例可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SharpDX.Direct3D11.Device&lt;/code&gt; 来找到）。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalDesc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalTexture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Texture2DDescription&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BindFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Usage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Staging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MipLevels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ArraySize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SampleDescription&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Quality&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImmediateContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalTexture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意，拷贝纹理会额外占用显存，一般不建议这么做，除非你真的有需求一定要 CPU 能够访问到这段纹理。&lt;/p&gt;

&lt;h2 id=&quot;导出成图片文件&quot;&gt;导出成图片文件&lt;/h2&gt;

&lt;p&gt;实际上，当你组合起来以上以上方法，你应该能够将纹理导出成图片了。&lt;/p&gt;

&lt;p&gt;不过，为了理解更方便一些，我还是将导出成图片的全部代码贴出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapTexture2DToFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Direct3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Texture2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 获取 Texture2D 的相关实例。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originDesc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 创建新的 Texture2D 对象。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Texture2DDescription&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CpuAccessFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Usage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Staging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;MipLevels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ArraySize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SampleDescription&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Quality&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OptionFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceOptionFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shared&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 拷贝资源。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImmediateContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DXGI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharpDX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DXGI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetPixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromArgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dataStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Unmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你是希望以纯软件的方式渲染到 WPF 中（WriteableBitmap），可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-high-performance-bitmap-rendering&quot;&gt;WPF 高性能位图渲染 WriteableBitmap 及其高性能用法示例&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;记得打开不安全代码开关哦！详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/allow-unsafe-code-in-dotnet-project&quot;&gt;如何在 .NET 项目中开启不安全代码（以便启用 unsafe fixed 等关键字）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/47328796/6233938&quot;&gt;c++ - How to access pixels data from ID3D11Texture2D? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gamedev.net/forums/topic/692196-sharpdx-directx11-how-to-add-normal-mapping/&quot;&gt;SharpDX Directx11 How to add normal mapping ? - Graphics and GPU Programming - GameDev.net&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/16020988/6233938&quot;&gt;directx 11 - How to create bitmap from Surface (SharpDX) - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api?redirectedfrom=MSDN&quot;&gt;Desktop Duplication API - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/44908867/6233938&quot;&gt;c# - Reading Datastream sharpDX Error all values are 0 - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/sharpdx/SharpDX-Samples/blob/master/Desktop/Direct3D11/MiniCube/Program.cs&quot;&gt;SharpDX-Samples/Program.cs at master · sharpdx/SharpDX-Samples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 06:07:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/map-directx-surface-to-bitmap.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/map-directx-surface-to-bitmap.html</guid>
        
        
        <category>directx</category>
        
        <category>sharpdx</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 实现 NTFS 文件系统的硬链接 mklink /J（Junction）</title>
        <description>&lt;p&gt;我们知道 Windows 系统 NTFS 文件系统提供了硬连接功能，可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 命令开启。如果能够通过代码实现，那么我们能够做更多有趣的事情。&lt;/p&gt;

&lt;p&gt;本文提供使用 .NET/C# 代码创建 NTFS 文件系统的硬连接功能（目录联接）。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;目录联接&quot;&gt;目录联接&lt;/h2&gt;

&lt;p&gt;以管理员权限启动 CMD（命令提示符），输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 命令可以得知 mklink 的用法。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MKLINK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录符号链接。默认为文件&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建硬链接而非符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录联接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新的符号链接名称。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新链接引用的路径&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;相对或绝对&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们本次要用 .NET/C# 代码实现的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;/J&lt;/code&gt; 目录联接。实现的效果像这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-19-17-45-41.png&quot; alt=&quot;目录联接&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些文件夹带有一个“快捷方式”的角标，似乎是另一些文件夹的快捷方式一样。但这些与快捷方式的区别在于，应用程序读取路径的时候，目录联接会成为路径的一部分。&lt;/p&gt;

&lt;p&gt;比如在 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\NuGet\&lt;/code&gt; 中创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;debug&lt;/code&gt; 目录联接，目标设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\DemoRepo\bin\Debug&lt;/code&gt;，那么，你在各种应用程序中使用以下两个路径将被视为同一个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\NuGet\debug\DemoRepo-1.0.0.nupkg&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\DemoRepo\bin\Debug\DemoRepo-1.0.0.nupkg&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;或者这种：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\NuGet\debug\publish\&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Walterlv\DemoRepo\bin\Debug\publish\&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用-netc-实现&quot;&gt;使用 .NET/C# 实现&lt;/h2&gt;

&lt;p&gt;本文的代码主要参考自 &lt;a href=&quot;https://www.codeproject.com/script/Membership/View.aspx?mid=1994253&quot;&gt;jeff.brown&lt;/a&gt; 在 &lt;a href=&quot;https://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET&quot;&gt;Manipulating NTFS Junction Points in .NET - CodeProject&lt;/a&gt; 一文中所附带的源代码。&lt;/p&gt;

&lt;p&gt;由于随时可能更新，所以你可以前往 GitHub 仓库打开此代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/blob/master/Walterlv.Demo.MkLink/Walterlv.Demo.MkLink/JunctionPoint.cs&quot;&gt;walterlv.demo/JunctionPoint.cs at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用-junctionpoint&quot;&gt;使用 JunctionPoint&lt;/h2&gt;

&lt;p&gt;如果希望在代码中创建目录联接，则直接使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JunctionPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\Developments&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 指定如果目录联接存在，则会覆盖掉原来的目录联接。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/11156754/6233938&quot;&gt;windows - What the C# equivalent of “mklink /J”? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET&quot;&gt;Manipulating NTFS Junction Points in .NET - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points&quot;&gt;Reparse Points - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 06:04:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/mklink-junction-in-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/mklink-junction-in-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 高性能位图渲染 WriteableBitmap 及其高性能用法示例</title>
        <description>&lt;p&gt;WPF 渲染框架并没有对外提供多少可以完全控制渲染的部分，目前可以做的有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;D3DImage，用来承载使用 DirectX 各个版本渲染内容的控件&lt;/li&gt;
  &lt;li&gt;WriteableBitmap，通过一段内存空间来指定如何渲染一个位图的图片&lt;/li&gt;
  &lt;li&gt;HwndHost，通过承载一个子窗口以便能叠加任何种类渲染的控件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文将解释如何最大程度压榨 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 在 WPF 下的性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何使用-writeablebitmap&quot;&gt;如何使用 WriteableBitmap&lt;/h2&gt;

&lt;p&gt;创建一个新的 WPF 项目，然后我们在 MainWindow.xaml 中编写一点可以用来显示 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 的代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.HighPerformanceBitmap.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.HighPerformanceBitmap&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WriteableBitmap - walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SizeToContent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WidthAndHeight&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Image&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1280&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;720&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了评估其性能，我决定绘制和渲染 4K 品质的位图，并通过以下步骤来评估：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompositionTarget.Rendering&lt;/code&gt; 逐帧渲染以评估其渲染帧率&lt;/li&gt;
  &lt;li&gt;使用 Benchmark 基准测试来测试内部各种不同方法的性能差异&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是，在 MainWindow.xaml.cs 中添加一些测试用的修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media.Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.HighPerformanceBitmap&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WriteableBitmap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WriteableBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3840&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2160&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;96.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;96.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PixelFormats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pbgra32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rendering&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CompositionTarget_Rendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompositionTarget_Rendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 在这里添加绘制位图的逻辑。&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDirtyRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Int32Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，我留了一行注释说即将添加绘制位图的逻辑，接下来我们的主要内容将从此展开。&lt;/p&gt;

&lt;h2 id=&quot;启用不安全代码&quot;&gt;启用不安全代码&lt;/h2&gt;

&lt;p&gt;为了获取最佳性能，我们需要开启不安全代码。为此，你需要修改一下你的项目属性。&lt;/p&gt;

&lt;p&gt;你可以阅读我的另一篇博客了解如何启用不安全代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/allow-unsafe-code-in-dotnet-project&quot;&gt;如何在 .NET 项目中开启不安全代码（以便启用 unsafe fixed 等关键字） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;简单点说就是在你的项目文件中添加下面这一行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;启用帧率测试&quot;&gt;启用帧率测试&lt;/h2&gt;

&lt;p&gt;接下来，我们需要添加一点点代码来评估 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 的性能：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  private readonly byte[] _empty4KBitmapArray = new byte[3840 * 2160 * 4];
&lt;/span&gt;
--  private void CompositionTarget_Rendering(object sender, EventArgs e)
&lt;span class=&quot;gi&quot;&gt;++  private unsafe void CompositionTarget_Rendering(object sender, EventArgs e)
&lt;/span&gt;    {
        var width = _bitmap.PixelWidth;
        var height = _bitmap.PixelHeight;

        _bitmap.Lock();

++      fixed (byte* ptr = _empty4KBitmapArray)
&lt;span class=&quot;gi&quot;&gt;++      {
++          var p = new IntPtr(ptr);
++          Buffer.MemoryCopy(ptr, _bitmap.BackBuffer.ToPointer(), _empty4KBitmapArray.Length, _empty4KBitmapArray.Length);
++      }
&lt;/span&gt;
        _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
        _bitmap.Unlock();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;嗯，就是将一个空的 4K 大小的数组中的内容复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 的位图缓存中。&lt;/p&gt;

&lt;h3 id=&quot;4k-脏区&quot;&gt;4K 脏区&lt;/h3&gt;

&lt;p&gt;虽然我们看不到任何可变的修改，不过 WriteableBitmap 可不这么认为。因为我们调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddDirtyRect&lt;/code&gt; 将整个位图空间都加入到了脏区中，这样 WPF 会重新渲染整幅位图。&lt;/p&gt;

&lt;p&gt;Visual Studio 中看到的 CPU 占用率大约维持在 16% 左右（跟具体机器相关）；并且除了一开始启动的时候之外，完全没有 GC（这点很重要），内存稳定在一个值上不再变化。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;也只有本文一开始提及的三种方法才可能做到渲染任何可能的图形的时候没有 GC&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-16-18-59.png&quot; alt=&quot;CPU 占用率和内存用量&quot; /&gt;&lt;/p&gt;

&lt;p&gt;查看界面渲染帧率可以发现跑满 60 帧没有什么问题（跟具体机器相关）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-16-23-06.png&quot; alt=&quot;帧率&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;小脏区&quot;&gt;小脏区&lt;/h3&gt;

&lt;p&gt;现在，我们把脏区的区域缩小为 100*100，同样看性能数据。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  _bitmap.AddDirtyRect(new Int32Rect(0, 0, 100, 100));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以发现 CPU 占用降低到一半（确实是大幅降低，但是跟像素数量并不成比例）；内存没有变化（废话，4K 图像是确定的）；帧率没有变化（废话，只要性能够，帧率就是满的）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-16-27-51.png&quot; alt=&quot;小脏区 CPU 占用率和内存用量&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-16-29-13.png&quot; alt=&quot;小脏区帧率&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;无脏区&quot;&gt;无脏区&lt;/h3&gt;

&lt;p&gt;现在，我们将脏区清零。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  _bitmap.AddDirtyRect(new Int32Rect(0, 0, 0, 0));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在完全没有脏区的时候，CPU 占用直接降为 0，这个性能提升还是非常恐怖的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-16-34-41.png&quot; alt=&quot;零脏区 CPU 占用率和内存用量&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;不渲染&quot;&gt;不渲染&lt;/h3&gt;

&lt;p&gt;如果我们不把 WriteableBitmap 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Source&lt;/code&gt; 属性，那么无论脏区多大，CPU 占用都是 0。&lt;/p&gt;

&lt;h3 id=&quot;脏区大小与-cpu-占用率之间的关系&quot;&gt;脏区大小与 CPU 占用率之间的关系&lt;/h3&gt;

&lt;p&gt;从前面的测试中我们可以发现，脏区的大小在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 的渲染里占了绝对的耗时。因此，我把脏区大小与 CPU 占用率之间的关系用图表的形式贴出来，这样可以直观地理解其性能差异。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;需要注意，CPU 占用率与机器性能强相关，因此其绝对占用没有意义，但相对大小则有参考价值。&lt;/em&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;脏区大小&lt;/th&gt;
      &lt;th&gt;CPU 占用率&lt;/th&gt;
      &lt;th&gt;帧率&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0*0&lt;/td&gt;
      &lt;td&gt;0.0%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1*1&lt;/td&gt;
      &lt;td&gt;5.1%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;16*9&lt;/td&gt;
      &lt;td&gt;5.7%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;160*90&lt;/td&gt;
      &lt;td&gt;6.0%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;320*180&lt;/td&gt;
      &lt;td&gt;6.5%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;640*360&lt;/td&gt;
      &lt;td&gt;6.9%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1280*720&lt;/td&gt;
      &lt;td&gt;7.5%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1920*1080&lt;/td&gt;
      &lt;td&gt;10.5%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2560*1440&lt;/td&gt;
      &lt;td&gt;12.3%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3840*2160&lt;/td&gt;
      &lt;td&gt;16.1%&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;根据这张表我么可以得出：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;脏区渲染是 CPU 占用的最大瓶颈（因为没有脏区仅剩内存拷贝的时候 CPU 占用为 0%）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是有一个需要注意的信息是——虽然 CPU 占用率受脏区影响非常大，但主线程却几乎没有消耗 CPU 占用。此占用基本上全是渲染线程的事。&lt;/p&gt;

&lt;p&gt;如果我们分析主线程的性能分布，可以发现内存拷贝现在是性能瓶颈：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-17-38-04.png&quot; alt=&quot;内存拷贝是性能瓶颈&quot; /&gt;&lt;/p&gt;

&lt;p&gt;后面我们会提到 WriteableBitmap 的渲染原理，也会说到这一点。&lt;/p&gt;

&lt;h2 id=&quot;启用基准测试benchmark&quot;&gt;启用基准测试（Benchmark）&lt;/h2&gt;

&lt;p&gt;不过，由于内存数据的拷贝和脏区渲染实际上可以分开到两个不同的线程，如果这两者不同步执行（可能执行次数还有差异）的情况下，内存拷贝也可能成为性能瓶颈的一部分。&lt;/p&gt;

&lt;p&gt;于是我将不同的内存拷贝方法进行一个基准测试，便于大家评估使用哪种方法来为 WriteableBitmap 提供渲染数据。&lt;/p&gt;

&lt;h3 id=&quot;使用-copymemory-拷贝内存&quot;&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyMemory&lt;/code&gt; 拷贝内存&lt;/h3&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [Benchmark(Description = &quot;CopyMemory&quot;)]
++  [Arguments(3840, 2160)]
++  [Arguments(100, 100)]
&lt;/span&gt;    public unsafe void CopyMemory(int width, int height)
    {
        _bitmap.Lock();

++      fixed (byte* ptr = _empty4KBitmapArray)
&lt;span class=&quot;gi&quot;&gt;++      {
++          var p = new IntPtr(ptr);
++          CopyMemory(_bitmap.BackBuffer, new IntPtr(ptr), (uint)_empty4KBitmapArray.Length);
++      }
&lt;/span&gt;
        _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
        _bitmap.Unlock();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CopyMemory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;使用-movememory-移动内存&quot;&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MoveMemory&lt;/code&gt; 移动内存&lt;/h3&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [Benchmark(Description = &quot;RtlMoveMemory&quot;)]
++  [Arguments(3840, 2160)]
++  [Arguments(100, 100)]
&lt;/span&gt;    public unsafe void RtlMoveMemory(int width, int height)
    {
        _bitmap.Lock();

++      fixed (byte* ptr = _empty4KBitmapArray)
&lt;span class=&quot;gi&quot;&gt;++      {
++          var p = new IntPtr(ptr);
++          MoveMemory(_bitmap.BackBuffer, new IntPtr(ptr), (uint)_empty4KBitmapArray.Length);
++      }
&lt;/span&gt;
        _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
        _bitmap.Unlock();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;RtlMoveMemory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveMemory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;使用-buffermemorycopy-拷贝内存&quot;&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Buffer.MemoryCopy&lt;/code&gt; 拷贝内存&lt;/h3&gt;

&lt;p&gt;需要注意，&lt;code class=&quot;highlighter-rouge&quot;&gt;Buffer.MemoryCopy&lt;/code&gt; 是 .NET Framework 4.6 才引入的 API，在 .NET Framework 后续版本以及 .NET Core 的所有版本才可以使用，更旧版本的 .NET Framework 没有这个 API。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [Benchmark(Baseline = true, Description = &quot;Buffer.MemoryCopy&quot;)]
++  [Arguments(3840, 2160)]
++  [Arguments(100, 100)]
&lt;/span&gt;    public unsafe void BufferMemoryCopy(int width, int height)
    {
        _bitmap.Lock();

++      fixed (byte* ptr = _empty4KBitmapArray)
&lt;span class=&quot;gi&quot;&gt;++      {
++          var p = new IntPtr(ptr);
++          Buffer.MemoryCopy(ptr, _bitmap.BackBuffer.ToPointer(), _empty4KBitmapArray.Length, _empty4KBitmapArray.Length);
++      }
&lt;/span&gt;
        _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
        _bitmap.Unlock();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;自己写-for-循环&quot;&gt;自己写 for 循环&lt;/h3&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  [Benchmark(Description = &quot;for for&quot;)]
++  [Arguments(3840, 2160)]
++  [Arguments(100, 100)]
&lt;/span&gt;    public unsafe void ForForCopy(int width, int height)
    {
        _bitmap.Lock();

++      var buffer = (byte*)_bitmap.BackBuffer.ToPointer();
&lt;span class=&quot;gi&quot;&gt;++      for (var j = 0; j &amp;lt; height; j++)
++      {
++          for (var i = 0; i &amp;lt; width; i++)
++          {
++              var pixel = buffer + j * width * 4 + i * 4;
++              *pixel = 0xff;
++              *(pixel + 1) = 0x7f;
++              *(pixel + 2) = 0x00;
++              *(pixel + 3) = 0xff;
++          }
++      }
&lt;/span&gt;
        _bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
        _bitmap.Unlock();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;基准测试数据&quot;&gt;基准测试数据&lt;/h3&gt;

&lt;p&gt;我们跑一次基准测试：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Mean&lt;/th&gt;
      &lt;th&gt;Error&lt;/th&gt;
      &lt;th&gt;StdDev&lt;/th&gt;
      &lt;th&gt;Median&lt;/th&gt;
      &lt;th&gt;Ratio&lt;/th&gt;
      &lt;th&gt;RatioSD&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CopyMemory&lt;/td&gt;
      &lt;td&gt;2.723 ms&lt;/td&gt;
      &lt;td&gt;0.0642 ms&lt;/td&gt;
      &lt;td&gt;0.1881 ms&lt;/td&gt;
      &lt;td&gt;2.677 ms&lt;/td&gt;
      &lt;td&gt;0.84&lt;/td&gt;
      &lt;td&gt;0.08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;RtlMoveMemory&lt;/td&gt;
      &lt;td&gt;2.659 ms&lt;/td&gt;
      &lt;td&gt;0.0740 ms&lt;/td&gt;
      &lt;td&gt;0.2158 ms&lt;/td&gt;
      &lt;td&gt;2.633 ms&lt;/td&gt;
      &lt;td&gt;0.82&lt;/td&gt;
      &lt;td&gt;0.08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Buffer.MemoryCopy&lt;/td&gt;
      &lt;td&gt;3.246 ms&lt;/td&gt;
      &lt;td&gt;0.0776 ms&lt;/td&gt;
      &lt;td&gt;0.2250 ms&lt;/td&gt;
      &lt;td&gt;3.200 ms&lt;/td&gt;
      &lt;td&gt;1.00&lt;/td&gt;
      &lt;td&gt;0.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;‘for for’&lt;/td&gt;
      &lt;td&gt;10.401 ms&lt;/td&gt;
      &lt;td&gt;0.1979 ms&lt;/td&gt;
      &lt;td&gt;0.4964 ms&lt;/td&gt;
      &lt;td&gt;10.396 ms&lt;/td&gt;
      &lt;td&gt;3.21&lt;/td&gt;
      &lt;td&gt;0.25&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;‘CopyMemory with 100*100 dirty region’&lt;/td&gt;
      &lt;td&gt;2.446 ms&lt;/td&gt;
      &lt;td&gt;0.0757 ms&lt;/td&gt;
      &lt;td&gt;0.2207 ms&lt;/td&gt;
      &lt;td&gt;2.368 ms&lt;/td&gt;
      &lt;td&gt;0.76&lt;/td&gt;
      &lt;td&gt;0.09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;‘RtlMoveMemory with 100*100 dirty region’&lt;/td&gt;
      &lt;td&gt;2.415 ms&lt;/td&gt;
      &lt;td&gt;0.0733 ms&lt;/td&gt;
      &lt;td&gt;0.2161 ms&lt;/td&gt;
      &lt;td&gt;2.369 ms&lt;/td&gt;
      &lt;td&gt;0.75&lt;/td&gt;
      &lt;td&gt;0.08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;‘Buffer.MemoryCopy with 100*100 dirty region’&lt;/td&gt;
      &lt;td&gt;3.076 ms&lt;/td&gt;
      &lt;td&gt;0.0612 ms&lt;/td&gt;
      &lt;td&gt;0.1523 ms&lt;/td&gt;
      &lt;td&gt;3.072 ms&lt;/td&gt;
      &lt;td&gt;0.95&lt;/td&gt;
      &lt;td&gt;0.08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;‘for for with 100*100 dirty region’&lt;/td&gt;
      &lt;td&gt;10.014 ms&lt;/td&gt;
      &lt;td&gt;0.2398 ms&lt;/td&gt;
      &lt;td&gt;0.6995 ms&lt;/td&gt;
      &lt;td&gt;9.887 ms&lt;/td&gt;
      &lt;td&gt;3.10&lt;/td&gt;
      &lt;td&gt;0.29&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;可以发现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CopyMemory&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;RtMoveMemory&lt;/code&gt; 性能是最好的，其性能差不多；&lt;/li&gt;
  &lt;li&gt;自己写循环拷贝内存的性能是最差的；&lt;/li&gt;
  &lt;li&gt;如果 WriteableBitmap 不渲染，那么无论设置多大的脏区都不会对性能有任何影响。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;结论和使用建议&quot;&gt;结论和使用建议&lt;/h2&gt;

&lt;p&gt;综合前面两者的结论，我们可以发现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;WriteableBitmap 的性能瓶颈源于对脏区的重新渲染
    &lt;ul&gt;
      &lt;li&gt;脏区为 0 或者不在可视化树渲染，则不消耗性能&lt;/li&gt;
      &lt;li&gt;只要有脏区，渲染过程就会开始成为性能瓶颈
        &lt;ul&gt;
          &lt;li&gt;CPU 占用基础值就很高了&lt;/li&gt;
          &lt;li&gt;脏区越大，CPU 占用越高，但增幅不大&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;内存拷贝不是 WriteableBitmap 的性能瓶颈
    &lt;ul&gt;
      &lt;li&gt;建议使用 Windows API 或者 .NET API 来拷贝内存（而不是自己写）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;另外，如果你有一些特殊的应用场景，可以适当调整下自己写代码的策略：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你希望有较大脏区的情况下降低 CPU 占用，可以考虑降低 WriteableBitmap 脏区的刷新率&lt;/li&gt;
  &lt;li&gt;如果你希望 WriteableBitmap 有较低的渲染延迟，则考虑减小脏区&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;writeablebitmap-渲染原理&quot;&gt;WriteableBitmap 渲染原理&lt;/h2&gt;

&lt;p&gt;在调用 WriteableBitmap 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddDirtyRect&lt;/code&gt; 方法的时候，实际上是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MILSwDoubleBufferedBitmap.AddDirtyRect&lt;/code&gt;，这是 WPF 专门为 WriteableBitmap 而提供的非托管代码的双缓冲位图的实现。&lt;/p&gt;

&lt;p&gt;在 WriteableBitmap 内部数组修改完毕之后，需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unlock&lt;/code&gt; 来解锁内部缓冲区的访问，这时会提交所有的修改。接下来的渲染都交给了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MediaContext&lt;/code&gt;，用来完成双缓冲位图的渲染。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SubscribeToCommittingBatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Only subscribe the the CommittingBatch event if we are on-channel.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_isWaitingForCommit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;MediaContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mediaContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MediaContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_duceResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsOnChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mediaContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mediaContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CommittingBatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommittingBatchHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_isWaitingForCommit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommittingBatchHandler&lt;/code&gt; 中，将渲染指令发送到了渲染线程。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SendCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DUCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前面我们通过脏区大小可以得出内存拷贝不是 CPU 占用率的瓶颈，脏区大小才是，不过是渲染线程在占用这 CPU 而不是主线程。但是内存拷贝却成为了主线程的瓶颈（当然前面我们给出了数据，实际上非常小）。所以如果试图分析这么高 CPU 的占用，会发现并不能从主线程上调查得出符合预期的结论（因为即便你完全干掉了内存拷贝，CPU 占用依然是这么高）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-17-17-38-04.png&quot; alt=&quot;内存拷贝是性能瓶颈&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 22 Oct 2019 04:43:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-high-performance-bitmap-rendering.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-high-performance-bitmap-rendering.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET Framework 4.x 程序到底运行在哪个 CLR 版本之上</title>
        <description>&lt;p&gt;当我们编译程序目标框架选为 .NET Framework 4.5/4.6/4.7/4.8 时，CLR 运行时是如何判断我们究竟应该用哪一个 .NET Framework 呢？.NET Framework 的版本到底由哪些部分组成？我们编译 .NET Framework 时选择的版本决定了什么？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;让我对这个问题产生兴趣的原因是：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;我将程序编译的目标框架选为 .NET Framework 4.8；在一台安装了 .NET Framework 4.6 的电脑上提示缺少 .NET Framework 4.8；删除了随编译一起生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 文件后程序能够正常运行。&lt;/li&gt;
  &lt;li&gt;另一个程序，我明明将程序编译的目标框架选为 .NET Framework 4.5，但在一台没有安装任何额外 .NET Framework 的 Windows 7 的电脑上提示缺少的是 .NET Framework 4.0。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里的疑点在于为什么以上两种看似类似的情况，提示的框架版本却不同。其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 文件成为了调查此问题的突破口。&lt;/p&gt;

&lt;h2 id=&quot;配置支持的运行时&quot;&gt;配置支持的运行时&lt;/h2&gt;

&lt;p&gt;观察程序附带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 文件，我们发现支持的运行时版本是 v4.0，sku 版本是 4.8。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;  
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;startup&amp;gt;&lt;/span&gt;  
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedRuntime&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;sku=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework,Version=v4.8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/startup&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;疑点：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;为什么我们基于 .NET Framework 4.8 开发的程序运行时版本是 4.0？&lt;/li&gt;
  &lt;li&gt;sku 是什么？&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;微软的官方文档给了我们解答：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/startup/supportedruntime-element?wt.mc_id=MVP&quot;&gt;supportedRuntime Element&lt;/a&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt;：用于指定此应用程序支持的公共语言运行时（CLR）的版本。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sku&lt;/code&gt;：stock-keeping unit（官方中文为“库存单位”，然而依然不懂这个词的意思），用于指定此应用程序支持的 .NET Framework 发行版本。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 的值可取：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;.NET Framework 版本&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 值&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;忽略早期版本&lt;/td&gt;
      &lt;td&gt;忽略早期版本&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;“v2.0.50727”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3.0&lt;/td&gt;
      &lt;td&gt;“v2.0.50727”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3.5&lt;/td&gt;
      &lt;td&gt;“v2.0.50727”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3.5&lt;/td&gt;
      &lt;td&gt;“v2.0.50727”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.0-4.8&lt;/td&gt;
      &lt;td&gt;“v4.0”&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sku&lt;/code&gt; 的值可取：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;.NET Framework version&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sku&lt;/code&gt; 值&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4.0&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.0”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;忽略中间版本&lt;/td&gt;
      &lt;td&gt;忽略中间版本&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.5”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.1&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.5.1”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.5.2&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.5.2”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.6”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.1&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.6.1”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.6.2&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.6.2”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.7”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.1&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.7.1”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.7.2&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.7.2”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4.8&lt;/td&gt;
      &lt;td&gt;“.NETFramework,Version=v4.8”&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;于是我们发现，其实无论我们将程序的目标框架选为 .NET Framework 的哪一个 4.x 版本，CLR 运行时都是用 v4.0 表示的。微软的描述是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;对于支持 .NET Framework 4.0 或更高版本的应用程序，version 属性指示 CLR 版本，这是 .NET Framework 4 及更高版本的通用版本，而 sku 属性指示应用程序所针对的单个 .NET Framework 版本。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其实看到这里我们就能有一个看似不错的解释：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;无论我们选择的目标框架是 .NET Framework 4.x 的哪一个版本，用于指定 CLR 运行时版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 值都是 v4.0；&lt;/li&gt;
  &lt;li&gt;CLR 运行时会根据配置文件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;sku&lt;/code&gt; 值决定应该采用那一组运行库来为程序运行提供支持。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;net-framework-的组成以及各部分的版本&quot;&gt;.NET Framework 的组成以及各部分的版本&lt;/h2&gt;

&lt;p&gt;我们需要寻找到 .NET Framework 的本质，不然如此错综复杂的版本号系统真把我搞懵了。&lt;/p&gt;

&lt;p&gt;微软在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies?wt.mc_id=MVP&quot;&gt;.NET Framework Versions and Dependencies&lt;/a&gt; 中说到：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;每个版本的 .NET Framework 都包含公共语言运行时 (CLR)、基础库和其他托管库。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是我们谈论 .NET Framework 的版本其实应该分三个不同的部分来谈：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;每个新版本的 .NET Framework 都会保留早期版本中的功能并会添加新功能。 CLR 有其自己的版本号标识。 虽然 CLR 版本并不总是递增的，但 .NET Framework 版本号在每次发布时都会递增。 例如，.NET Framework 4、4.5 和更高版本包含 CLR 4，而 .NET Framework 2.0、3.0 和 3.5 包含 CLR 2.0。 （没有版本 3 的 CLR。）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;从官方文档给出的表格当中我们可以确信：&lt;strong&gt;.NET Framework 4.0/4.5/4.6/4.7 包含的 CLR 版本都是 4.0。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;clr-的更新&quot;&gt;CLR 的更新&lt;/h2&gt;

&lt;p&gt;然而，不相信微软的 CLR 可以完全没有 BUG，既然 CLR 版本都是 4.0，那么微软对 CLR 运行时的更新怎么处理？安装了 .NET Framework 4.5/4.6/4.7 会如何提升 CLR 的稳定性和安全性？&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies#targeting-and-running-net-framework-apps-for-version-45-and-later?wt.mc_id=MVP&quot;&gt;Targeting and Running .NET Framework apps for version 4.5 and later&lt;/a&gt; 中，解释了 CLR 的更新机制——就地更新（in-place update）。这篇文章 &lt;a href=&quot;https://weblog.west-wind.com/posts/2012/Mar/13/NET-45-is-an-inplace-replacement-for-NET-40?wt.mc_id=MVP&quot;&gt;.NET 4.5 is an in-place replacement for .NET 4.0&lt;/a&gt; 对这种就地更新方式有比官方文档更详细的解释，并且还附带自己的一些试验（含代码）。不过文章是 2012 年写的，部分结论现在看来已经过时（因为在我的 Windows 10 配 .NET Framework 4.7 上结论已经不一样），不过对我理解就地更新本身非常有帮助，也为后续调查提供了更清晰的思路。&lt;/p&gt;

&lt;p&gt;微软对 .NET Framework 4.x 框架就地更新的说明是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;.NET Framework 4.5 是替代计算机上的 .NET Framework 4 的就地更新，同样，.NET Framework 4.5.1 4.5.2、4.6、4.6.1、4.6.2、4.7、4.7.1、4.7.2、4.8 是对 .NET Framework 4.5 的就地更新，这意味着它们将使用相同的运行时版本，但是程序集版本会更新并包括新类型和成员。 在安装其中某个更新后，你的 .NET Framework 4.NET Framework 4.5 或 .NET Framework 4.6 应用应继续运行，而无需重新编译。 但是，反过来则不行。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，&lt;strong&gt;无论我们在开发时指定目标框架的版本是 4.x 的哪一个，在运行时，CLR 环境都是 4.0&lt;/strong&gt;。但是新的 .NET Framework 会带来更新版本的 CLR，这个 CLR 会直接替换掉旧的 CLR。&lt;a href=&quot;https://weblog.west-wind.com/posts/2012/Mar/13/NET-45-is-an-inplace-replacement-for-NET-40&quot;&gt;.NET 4.5 is an in-place replacement for .NET 4.0&lt;/a&gt; 文章中 .NET Framework 基础库也是就地更新的；但我实际实验的情况是每一个不同的 .NET Framework 基础库有自己单独的文件夹，目前尚不清楚这个改变是从 .NET Framework 的哪一个版本开始的，但一定是 4.5.1、4.5.2、4.6 这三个版本中的一个。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-22-21-39-42.png&quot; alt=&quot;每一个不同的 .NET Framework 基础库有自己单独的文件夹&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决一开始的疑问&quot;&gt;解决一开始的疑问&lt;/h2&gt;

&lt;p&gt;于是，本文一开始的疑问就全部明晰了：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;不管是 .NET Framework 4.5 的还是 4.8 的那两个程序，都是靠 4.0 版本的公共语言运行时（CLR）运行起来的；&lt;/li&gt;
  &lt;li&gt;如果没有安装 4.0 版本的 CLR，则会弹出提示需要安装 .NET Framework 4.0 版本才能运行，而不管我们的程序目标框架是 .NET Framework 4.x 的哪一个版本；
    &lt;ul&gt;
      &lt;li&gt;虽然说文案说的是 .NET Framework，但其实需要的是 CLR&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果已经安装有 4.0 版本的 CLR（可能随 .NET Framework 4.5/4.6 安装），我们程序的目标框架是 .NET Framework 4.8，但 .NET Framework 基础库并没有安装 4.8 版本，则运行时会提示需要安装 .NET Framework 4.7；
    &lt;ul&gt;
      &lt;li&gt;这个提示是 4.0 版的 CLR 弹出的，是根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;supportedRuntime&lt;/code&gt; 中指定的 &lt;code class=&quot;highlighter-rouge&quot;&gt;sku&lt;/code&gt; 值来决定的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/startup/supportedruntime-element?wt.mc_id=MVP&quot;&gt;supportedRuntime Element - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies#targeting-and-running-net-framework-apps-for-version-45-and-later?wt.mc_id=MVP&quot;&gt;.NET Framework Versions and Dependencies - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblog.west-wind.com/posts/2012/Mar/13/NET-45-is-an-inplace-replacement-for-NET-40&quot;&gt;.NET 4.5 is an in-place replacement for .NET 4.0 - Rick Strahl’s Web Log&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17148496/what-does-sku-attribute-mean-in-c&quot;&gt;app config - What does “SKU” (attribute) mean in C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/21566528/what-happens-if-i-remove-the-auto-added-supportedruntime-element&quot;&gt;.net - What happens if I remove the auto added supportedRuntime element? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 16 Oct 2019 01:42:52 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2017/09/22/dotnet-version.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2017/09/22/dotnet-version.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 .editorconfig 配置 .NET/C# 项目的代码分析规则的严重程度</title>
        <description>&lt;p&gt;随着 Visual Studio 2019 更新，在 Visual Studio 中编写代码的时候也带来了基于 Roslyn 的代码质量分析。有一些代码分析严重程度可能与团队约定的不一致，这时就需要配置规则的严重程度。另外如果是个人使用插件安装了分析器，也可以配置一些严重程度满足个人的喜好。&lt;/p&gt;

&lt;p&gt;本文介绍使用 .editorconfig 文件来配置 .NET/C# 项目中，代码分析规则的严重性。可以是全局的，也可以每个项目有自己的配置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;生效范围与继承&quot;&gt;生效范围与继承&lt;/h2&gt;

&lt;p&gt;.editorconfig 文件可以在你的项目中的任何地方，甚至是代码仓库之外。是按照文件夹结构来继承生效的。&lt;/p&gt;

&lt;p&gt;比如我的项目结构是这样：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;editorconfig&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Foo.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;editorconfig&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Program.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-11-14-12.png&quot; alt=&quot;项目结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么 Foo.cs 文件的规则严重性将受 Core 文件夹中的 .editorconfig 文件管理，如果有些规则不在此文件夹的 .editorconfig 里面，就会受外层 .editorconfig 管理。&lt;/p&gt;

&lt;p&gt;另外，你甚至可以在整个代码仓库的外部文件夹放一个 .editorconfig 文件，这样，如果项目中没有对应的规则，那么外面文件夹中的 .editorconfig 规则就会生效，这相当于间接做了一个全局生效的规则集。&lt;/p&gt;

&lt;h2 id=&quot;editorconfig-中的内容&quot;&gt;.editorconfig 中的内容&lt;/h2&gt;

&lt;p&gt;.editorconfig 中的分析器严重性内容就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[*.cs]&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# CC0097: You have missing/unexistent parameters in Xml Docs
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_diagnostic.CC0097.severity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;error&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# CA1031: Do not catch general exception types
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_diagnostic.CA1031.severity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;suggestion&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# IDE0051: 删除未使用的私有成员
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_diagnostic.IDE0051.severity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于 C# 语言的规则，在 [*.cs] 区，每个规则格式是 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet_diagnostic.{DiagnosticId}.severity = {Severity}&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;当然，我们不需要手工书写这个文件，了解它的格式只是为了出问题的时候不至于一脸懵逼。&lt;/p&gt;

&lt;h2 id=&quot;配置严重程度&quot;&gt;配置严重程度&lt;/h2&gt;

&lt;p&gt;使用 Visual Studio 2019，配置规则严重性非常简单。当然，16.3 以上版本才这么简单，之前的版本步骤多一点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-12-11-32-25.png&quot; alt=&quot;配置规则严重性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在提示有问题的代码上按下重构快捷键（默认是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt;），可以出现重构菜单，其中就有配置规则严重性的选项，直接选择即可自动添加到 .editorconfig 文件中。如果项目中没有 .editorconfig 文件，则会自动在解决方案同目录下创建一个新的。&lt;/p&gt;

&lt;p&gt;对这部分快捷键不了解的话可以阅读：&lt;a href=&quot;/post/keyboard-shortcuts-to-improve-the-efficiency-of-visual-studio&quot;&gt;提高使用 Visual Studio 开发效率的键盘快捷键 - walterlv&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sat, 12 Oct 2019 03:35:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-editor-config-file-to-config-diagnostic-severities.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-editor-config-file-to-config-diagnostic-severities.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>什么是模态窗口？本文带你了解模态窗口的本质</title>
        <description>&lt;p&gt;做 Windows 桌面应用开发的小伙伴们对“模态窗口”（Modal Dialog）一定不陌生。如果你希望在模态窗口之上做更多的事情，或者自己实现一套模态窗口类似的机制，那么你可能需要了解模态窗口的本质。&lt;/p&gt;

&lt;p&gt;本文不会太深，只是从模态窗口一词出发，抵达大家都熟知的一些知识为止。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;开发中的模态窗口&quot;&gt;开发中的模态窗口&lt;/h2&gt;

&lt;p&gt;在各种系统、语言和框架中，只要有用户可以看见的界面，都存在模态窗口的概念。从交互层面来说，它的形式是在保留主界面作为环境来显示的情况下，打开一个新的界面，用户只能在新的界面上操作，完成或取消后才能返回主界面。从作用上来说，通常是要求用户必须提供某些信息后才能继续操作，或者单纯只是为了广告。&lt;/p&gt;

&lt;h2 id=&quot;模态窗口的三个特点&quot;&gt;模态窗口的三个特点&lt;/h2&gt;

&lt;p&gt;如果你希望自己搞一套模态窗口出来，那么只需要满足这三点即可。你可以随便加戏但那都无关紧要。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;保留主界面显示的同时，禁用主界面的用户交互；&lt;/li&gt;
  &lt;li&gt;显示子界面，主界面在子界面操作完成后返回；&lt;/li&gt;
  &lt;li&gt;当用户试图跳过子界面的交互的时候进行强提醒。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;拿 Windows 系统中的模态对话框为例子，大概就像下面这两张图片这样：&lt;/p&gt;

&lt;p&gt;有一个小的子界面盖住了主界面，要求用户必须进行选择。Windows 系统设置因为让背景变暗了，所以用户肯定会看得到需要进行的交互；而任务管理器没有让主界面变暗，所以用户在操作子界面的时候，模态窗口的边框和标题栏闪烁以提醒用户注意。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-08-09-01-47.png&quot; alt=&quot;Windows 系统设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-08-modal-dialog-twinkle.gif&quot; alt=&quot;任务管理器&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;实现模态窗口&quot;&gt;实现模态窗口&lt;/h2&gt;

&lt;p&gt;对于 Windows 操作系统来说，模态窗口并不是一个单一的概念，你并不能仅通过一个 API 调用就完成显示模态窗口，你需要在不同的时机调用不同的 API 来完成一个模态窗口。如果要完整实现一个自己的模态窗口，则需要编写实现以上三个特点的代码。&lt;/p&gt;

&lt;p&gt;当然，你可能会发现实际上你显示一个模态窗口仅仅一句话调用就够了，那是因为你所用的应用程序框架帮你完成了模态窗口的一系列机制。&lt;/p&gt;

&lt;p&gt;关于 WPF 框架是如何实现模态窗口的，可以阅读：&lt;a href=&quot;/post/how-does-wpf-implement-modal-dialog&quot;&gt;直击本质：WPF 框架是如何实现模态窗口的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于如何自己实现一个跨越线程/进程边界的模态窗口，可以阅读：&lt;a href=&quot;/post/implement-own-modal-dialogs-across-processes-or-threads&quot;&gt;实现 Windows 系统上跨进程/跨线程的模态窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果你希望定制以上第三个特点中强提醒的动画效果，可以阅读：&lt;a href=&quot;https://www.cnblogs.com/kybs0/p/7357759.html&quot;&gt;WPF window 子窗口反馈效果（抖动/阴影渐变) - 唐宋元明清2188 - 博客园&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;api-调用&quot;&gt;API 调用&lt;/h2&gt;

&lt;p&gt;为了在 Windows 上实现模态窗口，需要一些 Win32 API 调用（当然，框架够用的话直接用框架就好）。&lt;/p&gt;

&lt;h3 id=&quot;禁用主窗口&quot;&gt;禁用主窗口&lt;/h3&gt;

&lt;p&gt;我们需要使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;BOOL EnableWindow(HWND hWnd, BOOL bEnable);&lt;/code&gt; 来启用与禁用某个窗口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;EnableWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 模态显示一个窗口。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;EnableWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnableWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bEnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;阻塞代码等待操作完成&quot;&gt;阻塞代码等待操作完成&lt;/h3&gt;

&lt;p&gt;因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的出现，阻塞其实可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 来实现。虽然这不是真正的阻塞，但可以真实反应出“异步”这个过程，也就是虽然这里在等待，但实际上依然能够继续在同一个线程响应用户的操作。&lt;/p&gt;

&lt;p&gt;UWP 中的新 API 当然已经都是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 来实现模态等待了，不过 WPF/Windows Forms 比较早，只能使用 Dispatcher 线程模型来实现模态等待。&lt;/p&gt;

&lt;p&gt;于是我们可以考虑直接使用现成的 Dispatcher 线程模型来完成等待，方法是调用下面两个当中的任何一个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt; 也就是直接使用窗口原生的模态&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 新开一个消息循环以阻塞当前代码的同时继续响应 UI 交互&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt; 的本质也是在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt;，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-does-wpf-implement-modal-dialog&quot;&gt;直击本质：WPF 框架是如何实现模态窗口的&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 新开消息循环阻塞的原理可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，还有其他可以新开消息循环的方法。&lt;/p&gt;

&lt;h3 id=&quot;进行-ui-强提醒&quot;&gt;进行 UI 强提醒&lt;/h3&gt;

&lt;p&gt;由于我们一开始禁用了主窗口，所以如果用户试图操作主窗口是不会有效果的。然而如果用户不知道当前显示了一个模态窗口需要操作，那么给出提醒也是必要的。&lt;/p&gt;

&lt;p&gt;简单的在 UI 上的提醒是最简单的了，比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;将主界面变暗（UWP 应用，Web 应用喜欢这么做）&lt;/li&gt;
  &lt;li&gt;将主界面变模糊（iOS 应用喜欢这么做）&lt;/li&gt;
  &lt;li&gt;在模态窗口上增加一个很厚重的阴影（Android 应用喜欢这么做）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而 Windows 和 Mac OS 这些古老的系统由于兼容性负担不能随便那么改，于是需要有其他的提醒方式。&lt;/p&gt;

&lt;p&gt;Windows 采用的方式是让标题栏闪烁，让阴影闪烁。&lt;/p&gt;

&lt;p&gt;而这些特效的处理，来自于子窗口需要处理一些特定的消息 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_SETCURSOR&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;详见：&lt;a href=&quot;https://www.cnblogs.com/kybs0/p/7357759.html&quot;&gt;WPF window 子窗口反馈效果（抖动/阴影渐变) - 唐宋元明清2188 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;通常你不需要手工处理这些消息，但是如果你完全定制了窗口样式，则可能需要自行做一个这样的模态窗口提醒效果。&lt;/p&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 11:28:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/what-is-a-modal-dialog.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/what-is-a-modal-dialog.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>直击本质：WPF 框架是如何实现模态窗口的</title>
        <description>&lt;p&gt;想知道你在 WPF 编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog()&lt;/code&gt; 之后，WPF 框架是如何帮你实现模态窗口的吗？&lt;/p&gt;

&lt;p&gt;本文就带你来了解这一些。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windowshowdialog&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;WPF 显示模态窗口的方法就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt;，因此我们直接进入这个方法查看。由于 .NET Core 版本的 WPF 已经开源，我们会使用 .NET Core 版本的 WPF 源代码。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt; 的源代码可以在这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#PresentationFramework/System/Windows/Window.cs,61d39b218b53dbbe&quot;&gt;Window.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个方法非常长，所以我只把其中与模态窗口最关键的代码和相关注释留下，其他都删除（这当然是不可编译的）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShowDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// NOTE:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// _threadWindowHandles is created here.  This reference is nulled out in EnableThreadWindows&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// when it is called with a true parameter.  Please do not null it out anywhere else.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// EnableThreadWindow(true) is called when dialog is going away.  Once dialog is closed and&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// thread windows have been enabled, then there no need to keep the array list around.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Please see BUG 929740 before making any changes to how _threadWindowHandles works.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_threadWindowHandles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//Get visible and enabled windows in the thread&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If the callback function returns true for all windows in the thread, the return value is true.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If the callback function returns false on any enumerated window, or if there are no windows&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// found in the thread, the return value is false.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No need for use to actually check the return value.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumThreadWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumThreadWindowsCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadWindowsCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullHandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//disable those windows&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;EnableThreadWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_showingAsDialog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// NOTE:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// See BUG 929740.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// _threadWindowHandles is created before calling ShowDialog and is deleted in&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// EnableThreadWindows (when it's called with true).&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Window dlg = new Window();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Button b = new button();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// b.OnClick += new ClickHandler(OnClick);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// dlg.ShowDialog();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// void OnClick(...)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// {&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//      dlg.Close();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//      throw new Exception();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// }&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// If above code is written, then we get inside this exception handler only after the dialog&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// is closed.  In that case all the windows that we disabled before showing the dialog have already&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// been enabled and _threadWindowHandles set to null in EnableThreadWindows.  Thus, we don't&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// need to do it again.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// In any other exception cases, we get in this handler before Dialog is closed and thus we do&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// need to enable all the disable windows.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_threadWindowHandles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Some exception case. Re-enable the windows that were disabled&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;EnableThreadWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;觉得代码还是太长？不要紧，我再简化一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EnumThreadWindows&lt;/code&gt; 获取当前线程的所有窗口&lt;/li&gt;
  &lt;li&gt;把当前线程中的所有窗口都禁用掉（用的是 Win32 API 的禁用哦，这不会导致窗口内控件的样式变为禁用状态）&lt;/li&gt;
  &lt;li&gt;将窗口显示出来（如果出现异常，则还原之前禁用的窗口）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;可以注意到禁用掉的窗口是“当前线程”的哦。&lt;/p&gt;

&lt;h2 id=&quot;showhelper&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ShowHelper&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;接下来的重点方法是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.ShowDialog&lt;/code&gt; 中的那句 &lt;code class=&quot;highlighter-rouge&quot;&gt;Show()&lt;/code&gt;。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Show()&lt;/code&gt; 之前设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;_showingAsDialog&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，于是这里会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowHelper&lt;/code&gt; 方法并传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;下面的代码也是精简后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowHelper&lt;/code&gt; 方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShowHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;booleanBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// tell users we're going modal&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ComponentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_dispatcherFrame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_dispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// tell users we're going non-modal&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ComponentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PopModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，重点是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushModal&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;PopModal&lt;/code&gt; 以及 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 的效果就是让调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 的代码看起来就像阻塞了一样（实际上就是阻塞了，只不过开了新的消息循环看起来 UI 不卡）。&lt;/p&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 为什么能够“阻塞”你的代码的同时还能继续响应 UI 操作的原理，可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;ComponentDispatcher.PushModal&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ComponentDispatcher.PopModal&lt;/code&gt; 呢？可以在这里（&lt;a href=&quot;https://source.dot.net/#WindowsBase/System/Windows/Interop/ComponentDispatcherThread.cs,60a128f40eff98b3&quot;&gt;ComponentDispatcherThread.cs&lt;/a&gt;）看它的代码，实际上是为了模态计数以及引发事件的，对模态的效果没有本质上的影响。&lt;/p&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 10:57:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-does-wpf-implement-modal-dialog.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-does-wpf-implement-modal-dialog.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET/C# 检测电脑上安装的 .NET Framework 的版本</title>
        <description>&lt;p&gt;如果你希望知道某台计算机上安装了哪些版本的 .NET Framework，那么正好本文可以帮助你解决问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何找到已安装的-net-framework&quot;&gt;如何找到已安装的 .NET Framework&lt;/h2&gt;

&lt;p&gt;有的电脑的 .NET Framework 是自带的，有的是操作系统自带的。这样，你就不能通过控制面板的“卸载程序”去找到到底安装了哪个版本的 .NET Framework 了。&lt;/p&gt;

&lt;p&gt;关于各个版本 Windows 10 上自带的 .NET Framework 版本，可以阅读 &lt;a href=&quot;/post/embeded-dotnet-version-in-all-windows&quot;&gt;各个版本 Windows 10 系统中自带的 .NET Framework 版本 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;而如果通过代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.Version&lt;/code&gt; 来获取 .NET 版本，实际上获取的是 CLR 的版本，详见 &lt;a href=&quot;/post/powershell/2017/09/28/get-clr-version-via-powershell.html&quot;&gt;使用 PowerShell 获取 CLR 版本号 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这些版本号是不同的，详见 &lt;a href=&quot;/dotnet/2017/09/22/dotnet-version.html&quot;&gt;.NET Framework 4.x 程序到底运行在哪个 CLR 版本之上 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;那么如何获取已安装的 .NET Framework 的版本呢？最靠谱的方法竟然是通过读取注册表。&lt;/p&gt;

&lt;h2 id=&quot;注册表位置和含义&quot;&gt;注册表位置和含义&lt;/h2&gt;

&lt;p&gt;读取位置在这里：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;计算机&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Setup\NDP\v4\Full\2052&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-10-14-57-02.png&quot; alt=&quot;注册表位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而唯一准确能够判定 .NET Framework 版本的，只有里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt; 值。但可惜的是，这个值并不能直接看出来到底是 4.5 还是 4.8。我们需要有一张对应表。&lt;/p&gt;

&lt;p&gt;我把它整理成了字典和注释，这样会比较容易理解每个编号对应的 .NET Framework 版本代号。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 获取 .NET Framework 4.5 及以上版本的发行号与版本名称的对应关系。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 4.5 及以下版本没有这样的对应关系。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseToNameDictionary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.5&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;378389&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.5&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.5.1（Windows 8.1 或 Windows Server 2012 R2 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;378675&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.5.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.5.1（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;378758&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.5.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.5.2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;379893&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.5.2&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6（Windows 10 第一个版本 1507 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;393295&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;393297&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6.1（Windows 10 十一月更新 1511 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;394254&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6.1（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;394271&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6.2（Windows 10 一周年更新 1607 和 Windows Server 2016 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;394802&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6.2&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.6.2（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;394806&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.6.2&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7（Windows 10 创造者更新 1703 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;460798&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;460805&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7.1（Windows 10 秋季创造者更新 1709 和 Windows Server 1709 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;461308&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7.1（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;461310&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7.1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7.2（Windows 10 2018年四月更新 1803 和 Windows Server 1803 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;461808&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7.2&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.7.2（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;461814&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.7.2&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.8（Windows 10 2019年五月更新 1903 自带）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;528040&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.8&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .NET Framework 4.8（其他系统安装）&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;528049&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.8&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，还有一些值也是有意义的（只是不那么精确）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主版本
    &lt;ul&gt;
      &lt;li&gt;也就是可以共存的版本，比如 v3.5 系列和 v4 系列就是可以共存的，它们分别是就地更新的保持兼容的版本&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;发行版本名称
    &lt;ul&gt;
      &lt;li&gt;完整版 Full 和精简版 Client&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;版本号
    &lt;ul&gt;
      &lt;li&gt;比如 3.5.30729.4926 或者 4.7.02556&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;服务包版本
    &lt;ul&gt;
      &lt;li&gt;古时候的微软喜欢用 SP1 SP2 来命名同一个版本的多次更新，这也就是那个年代的产物&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;它们分别在注册表的这些位置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主版本
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP&lt;/code&gt; 里项的名称&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;发行版本名称
    &lt;ul&gt;
      &lt;li&gt;以上项里子项的名称&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;版本号
    &lt;ul&gt;
      &lt;li&gt;以上项里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 值&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;服务包版本
    &lt;ul&gt;
      &lt;li&gt;以上项里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SP&lt;/code&gt; 值&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;读取注册表&quot;&gt;读取注册表&lt;/h2&gt;

&lt;p&gt;在上面已经梳理了读取注册表的位置之后，相信你可以很容易写出读取已安装 .NET Framework 版本的代码出来。&lt;/p&gt;

&lt;p&gt;我已经将其做成了 NuGet 源代码包（&lt;a href=&quot;https://blog.lindexi.com/post/sourceyard-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;使用 SourceYard 打包&lt;/a&gt;），你可以安装 NuGet 包来获得读取已安装 .NET Framework 版本的功能：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.Environment.Source/&quot;&gt;NuGet Gallery - Walterlv.Environment.Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;或者在 GitHub 查看源代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/blob/master/src/Utils/Walterlv.Environment/NdpInfo.cs&quot;&gt;Walterlv.Packages/NdpInfo.cs at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只有一个类型——&lt;code class=&quot;highlighter-rouge&quot;&gt;NdpInfo&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;使用方法有两种。&lt;/p&gt;

&lt;p&gt;第一种，获取当前计算机上所有已经安装的 .NET Framework 版本：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allVersions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NdpInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadFromRegistryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行完成之后看看得到的字典 &lt;code class=&quot;highlighter-rouge&quot;&gt;allVersions&lt;/code&gt; 如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-10-16-39-25.png&quot; alt=&quot;已安装的全部 .NET Framework&quot; /&gt;&lt;/p&gt;

&lt;p&gt;字典里 Key 是不能共存的主版本，Value 是这个主版本里当前已经安装的具体版本信息。&lt;/p&gt;

&lt;p&gt;如果直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt;，是可以生成我们平时经常在各大文档或者社区使用的 .NET Framework 的名称。&lt;/p&gt;

&lt;p&gt;第二种，获取当前已安装的最新的 .NET Framework 版本名称：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NdpInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentVersionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这可以直接获取到一个字符串，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;.NET Framework 4.8&lt;/code&gt;。对于只是简单获取一下已安装名称而不用做更多处理的程序来说会比较方便。&lt;/p&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 08:51:58 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detect-dotnet-framework-version-of-windows-system.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detect-dotnet-framework-version-of-windows-system.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 系统上用 .NET/C# 查找所有窗口，并获得窗口的标题、位置、尺寸、最小化、可见性等各种状态</title>
        <description>&lt;p&gt;在 Windows 应用开发中，如果需要操作其他的窗口，那么可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt; 这个 API 来枚举这些窗口。&lt;/p&gt;

&lt;p&gt;你可以使用本文编写的一个类型，查找到所有窗口中你关心的信息。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;需要使用的-api&quot;&gt;需要使用的 API&lt;/h2&gt;

&lt;p&gt;枚举所有窗口仅需要使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt;，其中需要定义一个委托 &lt;code class=&quot;highlighter-rouge&quot;&gt;WndEnumProc&lt;/code&gt; 作为传入参数的类型。&lt;/p&gt;

&lt;p&gt;剩下的我们需要其他各种方法用于获取窗口的其他属性。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetParent&lt;/code&gt; 获取窗口的父窗口，这可以确认找到的窗口是否是顶层窗口。（关于顶层窗口，可以延伸 &lt;a href=&quot;/post/all-processes-freezes-if-their-windows-are-connected-via-setparent&quot;&gt;使用 SetParent 跨进程设置父子窗口时的一些问题（小心卡死） - walterlv&lt;/a&gt;。）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IsWindowVisible&lt;/code&gt; 判断窗口是否可见&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetWindowText&lt;/code&gt; 获取窗口标题&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetClassName&lt;/code&gt; 获取窗口类名&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetWindowRect&lt;/code&gt; 获取窗口位置和尺寸，为此我们还需要定义一个结构体 &lt;code class=&quot;highlighter-rouge&quot;&gt;LPRECT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndEnumProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndEnumProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpEnumFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsWindowVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPRECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LPRECT&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;枚举所有窗口&quot;&gt;枚举所有窗口&lt;/h2&gt;

&lt;p&gt;我将以上 API 封装成 &lt;code class=&quot;highlighter-rouge&quot;&gt;FindAll&lt;/code&gt; 函数，并提供过滤器可以给大家过滤众多的窗口使用。&lt;/p&gt;

&lt;p&gt;比如，我写了下面一个简单的示例，可以输出当前可见的所有窗口以及其位置和尺寸：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WindowDetector&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$@&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;. &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-10-12-06-19.png&quot; alt=&quot;获取所有窗口以及其位置和尺寸&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FindAll&lt;/code&gt; 方法，我提供了一个默认参数，可以指定如何过滤所有枚举到的窗口。如果不指定，则会找可见的，包含标题的，没有最小化的窗口。如果你希望找一些看不见的窗口，可以自己写过滤条件。&lt;/p&gt;

&lt;p&gt;什么都不要过滤的话，就传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;_ =&amp;gt; true&lt;/code&gt;，意味着所有的窗口都会被枚举出来。&lt;/p&gt;

&lt;h2 id=&quot;附源码&quot;&gt;附源码&lt;/h2&gt;

&lt;p&gt;因为源代码会经常更新，所以建议在这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.WindowDetector/Walterlv.WindowDetector&quot;&gt;walterlv.demo/Walterlv.WindowDetector/Walterlv.WindowDetector at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;无法访问的话，可以看下面：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Drawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WindowDetector&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含枚举当前用户空间下所有窗口的方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowEnumerator&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 查找当前用户空间下所有符合条件的窗口。如果不指定条件，将仅查找可见窗口。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;match&quot;&amp;gt;过滤窗口的条件。如果设置为 null，将仅查找可见窗口。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;找到的所有窗口信息。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultPredicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 仅查找顶层窗口。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 获取窗口类名。&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;GetClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;className&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// 获取窗口标题。&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;GetWindowText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// 获取窗口可见性。&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsWindowVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// 获取窗口位置和尺寸。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;LPRECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;GetWindowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// 添加到已找到的窗口列表。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 默认的查找窗口的过滤条件。可见 + 非最小化 + 包含窗口标题。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultPredicate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsMinimized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndEnumProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndEnumProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpEnumFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsWindowVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SwitchToThisWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fAltTab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPRECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LPRECT&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 获取 Win32 窗口的一些基本信息。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowInfo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ClassName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取窗口句柄。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取窗口类名。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClassName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取窗口标题。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取当前窗口是否可见。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取窗口当前的位置和尺寸。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取窗口当前是否是最小化的。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsMinimized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;32000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;32000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 04:11:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/enumerate-all-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/enumerate-all-windows.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 WPF 程序中应用 Windows 10 真•亚克力效果</title>
        <description>&lt;p&gt;从 Windows 10 (1803) 开始，Win32 应用也可以有 API 来实现原生的亚克力效果了。不过相比于 UWP 来说，可定制性会差很多。&lt;/p&gt;

&lt;p&gt;本文介绍如何在 WPF 程序中应用 Windows 10 真•亚克力效果。（而不是一些流行的项目里面自己绘制的亚克力效果。）&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;api&quot;&gt;API&lt;/h2&gt;

&lt;p&gt;需要使用的 API 是微软的文档中并未公开的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我在另一篇博客中有介绍此 API 各种用法的效果，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/set-window-composition-attribute&quot;&gt;使用 SetWindowCompositionAttribute 来控制程序的窗口边框和背景（可以做 Acrylic 亚克力效果、模糊效果、主题色效果等） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，使用此 API 也可以做 Windows 10 早期的模糊效果，比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&quot;&gt;在 Windows 10 上为 WPF 窗口添加模糊特效（就像开始菜单和操作中心那样） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如何使用&quot;&gt;如何使用&lt;/h2&gt;

&lt;p&gt;为了方便地让你的窗口获得亚克力效果，我做了两层不同的 API：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AcrylicBrush&lt;/code&gt; &lt;em&gt;当然，受到 Win32 启用亚克力效果的限制，只能在窗口上设置此属性&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowAccentCompositor&lt;/code&gt; &lt;em&gt;用于更多地控制窗口与系统的叠加组合效果&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;代码请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/blob/master/src/Themes/Walterlv.Themes.FluentDesign/Effects/WindowAccentCompositor.cs&quot;&gt;Walterlv.Packages/WindowAccentCompositor.cs at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-10-10-25-00.png&quot; alt=&quot;亚克力效果在 WPF 程序中&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;

&lt;p&gt;要使得亚克力效果可以生效，需要：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;设置一个混合色 &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;混合色不能是全透明（如果全透明，窗口的亚克力部分就全透明穿透了），当然也不能全不透明，这样就看不到亚克力效果了。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/32724187/6233938&quot;&gt;winapi - How do you set the glass blend colour on Windows 10? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 02:25:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/using-acrylic-in-wpf-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/using-acrylic-in-wpf-application.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 SetWindowCompositionAttribute 来控制程序的窗口边框和背景（可以做 Acrylic 亚克力效果、模糊效果、主题色效果等）</title>
        <description>&lt;p&gt;Windows 系统中有一个没什么文档的 API，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt;，可以允许应用的开发者将自己窗口中的内容渲染与窗口进行组合。这可以实现很多系统中预设的窗口特效，比如 Windows 7 的毛玻璃特效，Windows 8/10 的前景色特效，Windows 10 的模糊特效，以及 Windows 10 1709 的亚克力（Acrylic）特效。而且这些组合都发生在 dwm 进程中，不会额外占用应用程序的渲染性能。&lt;/p&gt;

&lt;p&gt;本文介绍 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; 可以实现的所有效果。你可以通过阅读本文了解到与系统窗口可以组合渲染到哪些程度。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;试验用的源代码&quot;&gt;试验用的源代码&lt;/h2&gt;

&lt;p&gt;本文将创建一个简单的 WPF 程序来验证 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; 能达到的各种效果。你也可以不使用 WPF，得到类似的效果。&lt;/p&gt;

&lt;p&gt;简单的项目文件结构是这样的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;[项目] Walterlv.WindowComposition
    &lt;ul&gt;
      &lt;li&gt;App.xaml&lt;/li&gt;
      &lt;li&gt;App.xaml.cs&lt;/li&gt;
      &lt;li&gt;MainWindow.xaml&lt;/li&gt;
      &lt;li&gt;MainWindow.xaml.cs&lt;/li&gt;
      &lt;li&gt;WindowAccentCompositor&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，App.xaml 和 App.xaml.cs 保持默认生成的不动。&lt;/p&gt;

&lt;p&gt;为了验证此 API 的效果，我需要将 WPF 主窗口的背景色设置为纯透明或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，而设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 才能彻彻底底确保所有的样式一定是受我们自己控制的，我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 中没有指定任何可以显示的内容。MainWindow.xaml 的全部代码如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.WindowComposition.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问吕毅的博客：blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.Template&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AdornerDecorator&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AdornerDecorator&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.Template&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 我们注释掉 WindowChrome，是因为即将验证 WindowChrome 带来的影响。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;WindowChrome.WindowChrome&amp;gt;
        &amp;lt;WindowChrome GlassFrameThickness=&quot;-1&quot; /&amp;gt;
    &amp;lt;/WindowChrome.WindowChrome&amp;gt;--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而 MainWindow.xaml.cs 中，我们简单调用一下我们即将写的调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; 的类型。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows.Effects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WindowComposition&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowAccentCompositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromRgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0x18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xa0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x5e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还剩下一个 WindowAccentCompositor.cs 文件，因为比较长放到博客里影响阅读，所以建议前往这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/blob/master/src/Themes/Walterlv.Themes.FluentDesign/Effects/WindowAccentCompositor.cs&quot;&gt;Walterlv.Packages/WindowAccentCompositor.cs at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而其中对我们最终渲染效果有影响的就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;AccentPolicy&lt;/code&gt; 类型的几个属性。其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;AccentState&lt;/code&gt; 属性是下面这个枚举，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 将决定窗口渲染时叠加的颜色。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_DISABLED&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_GRADIENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_TRANSPARENTGRADIENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_BLURBEHIND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ACCENT_INVALID_STATE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;影响因素&quot;&gt;影响因素&lt;/h2&gt;

&lt;p&gt;经过试验，对最终显示效果有影响的有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;选择的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AccentState&lt;/code&gt; 枚举值&lt;/li&gt;
  &lt;li&gt;使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加色&lt;/li&gt;
  &lt;li&gt;是否使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 让客户区覆盖非客户区&lt;/li&gt;
  &lt;li&gt;目标操作系统（Windows 7/8/8.1/10）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，你可以用你自己的 UI 覆盖掉系统的 UI 窗口样式。关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 让客户区覆盖非客户区的知识，可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/dino623/p/uielements_of_window.html&quot;&gt;[WPF 自定义控件] Window（窗体）的 UI 元素及行为 - dino.c - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要注意的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt; 属性可以设置窗口边框的粗细，设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 将导致窗口没有阴影，设置为负数将使得整个窗口都是边框。&lt;/p&gt;

&lt;h2 id=&quot;排列组合&quot;&gt;排列组合&lt;/h2&gt;

&lt;p&gt;我们依次来看看效果。&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_disabled&quot;&gt;AccentState=ACCENT_DISABLED&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_DISABLED&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加色没有任何影响，唯一影响渲染的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 和操作系统。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，在 Windows 10 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-05-49.png&quot; alt=&quot;without WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-08-37.png&quot; alt=&quot;without WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 10 上，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-18-50.png&quot; alt=&quot;with WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 7 上，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-25-34.png&quot; alt=&quot;with WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，以上边框比较细，跟系统不搭，可以设置成其他值：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-23-51.png&quot; alt=&quot;bold thickness WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 10 上，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 并且 &lt;code class=&quot;highlighter-rouge&quot;&gt;GlassFrameThickness&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;-1&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-11-26.png&quot; alt=&quot;-1 glass frame in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;而在 Windows 7 上，这就是非常绚丽的全窗口的 Aero 毛玻璃特效：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-14-58.png&quot; alt=&quot;-1 glass frame in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_enable_gradient&quot;&gt;AccentState=ACCENT_ENABLE_GRADIENT&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_DISABLED&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加色会影响到最终的渲染效果。&lt;/p&gt;

&lt;p&gt;还记得我们前面叠加的颜色是什么吗？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-34-36.png&quot; alt=&quot;叠加的颜色&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来别忘了然后把它误以为是我系统的主题色哦！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，在 Windows 10 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-32-19.png&quot; alt=&quot;without WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;另外，你会注意到左、下、右三个方向上边框会深一些。那是 Windows 10 的窗口阴影效果，因为实际上 Windows 10 叠加的阴影也是窗口区域的一部分，只是一般人看不出来而已。我们叠加了颜色之后，这里就露馅儿了。&lt;/p&gt;

&lt;p&gt;另外，这个颜色并不是我们自己的进程绘制的哦，是 dwm 绘制的颜色。&lt;/p&gt;

&lt;p&gt;如果不指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 也就是保持为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，你将看到上面绿色的部分全是黑色的；嗯，包括阴影的部分……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-39-23.png&quot; alt=&quot;without WindowChrome in Windows 10 - default gradient color&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-08-37.png&quot; alt=&quot;without WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看出，在 Windows 7 上，&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 被无视了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 10 上，则可以得到整个窗口的叠加色：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16 48 16 16&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-windowchrome-gradient.gif&quot; alt=&quot;with WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以注意到，窗口获得焦点的时候，整个窗口都是叠加色；而窗口失去焦点的时候，指定了边框的部分颜色会更深（换其他颜色叠加可以看出来是叠加了半透明黑色）。&lt;/p&gt;

&lt;p&gt;如果你希望失去焦点的时候，边框部分不要变深，请将边框设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;-1&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上，依然没有任何叠加色的效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-29-44.png&quot; alt=&quot;with WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_enable_transparentgradient&quot;&gt;AccentState=ACCENT_ENABLE_TRANSPARENTGRADIENT&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_ENABLE_TRANSPARENTGRADIENT&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加色没有任何影响，唯一影响渲染的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 和操作系统。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，在 Windows 10 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-gradient-transparent.gif&quot; alt=&quot;without WindowChrome frame in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依然左、下、右三个方向上边框会深一些，那是 Windows 10 的窗口阴影效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-08-37.png&quot; alt=&quot;without WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 也是被无视的，而且效果跟之前一样。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 10 上，在获得焦点的时候整个背景是系统主题色；而失去焦点的时候是灰色，但边框部分是深色。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-windowchrome-frame-gradient-transparent.gif&quot; alt=&quot;with WindowChrome frame in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依然可以将边框设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;-1&lt;/code&gt; 使得边框不会变深：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-windowchrome-gradient-transparent.gif&quot; alt=&quot;with WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上，依然是老样子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-29-44.png&quot; alt=&quot;with WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_enable_blurbehind&quot;&gt;AccentState=ACCENT_ENABLE_BLURBEHIND&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_ENABLE_BLURBEHIND&lt;/code&gt; 可以在 Windows 10 上做出模糊效果，就跟 Windows 10 早期版本的模糊效果是一样的。你可以看我之前的一篇博客，那时亚克力效果还没出来：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&quot;&gt;在 Windows 10 上为 WPF 窗口添加模糊特效（就像开始菜单和操作中心那样） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_ENABLE_BLURBEHIND&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加色没有任何影响，唯一影响渲染的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 和操作系统。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 10 上，没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-15-37.png&quot; alt=&quot;模糊效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可能需要留意一下那个“诡异”的模糊范围，你会发现窗口的阴影外侧也是有模糊的！！！你能忍吗？肯定不能忍，所以还是乖乖使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 7 上，没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，效果跟其他值一样，依然没有变化：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-08-37.png&quot; alt=&quot;without WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 10 上，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-26-43.png&quot; alt=&quot;with WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 在 Windows 7 上，依然是老样子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-29-44.png&quot; alt=&quot;with WindowChrome in Windows 7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_enable_acrylicblurbehind&quot;&gt;AccentState=ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/h3&gt;

&lt;p&gt;从 Windows 10 (1803) 开始，Win32 程序也能添加亚克力效果了，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; 的参数枚举新增了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;亚克力效果相信大家不陌生，那么在 Win32 应用程序里面使用的效果是什么呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;，在 Windows 10 上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-20-32-19.png&quot; alt=&quot;without WindowChrome in Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;咦！等等！这不是跟之前一样吗？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;嗯，下面就是不同了，亚克力效果支持与半透明的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientColor&lt;/code&gt; 叠加，所以我们需要将传入的颜色修改为半透明：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    var compositor = new WindowAccentCompositor(this);
&lt;span class=&quot;gd&quot;&gt;--  compositor.Composite(Color.FromRgb(0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  compositor.Composite(Color.FromArgb(0x3f, 0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-39-26.png&quot; alt=&quot;acrylic without WindowChrome&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;那么如果改为全透明会怎么样呢？&lt;/p&gt;

&lt;p&gt;不幸的是，完全没有效果！！！&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    var compositor = new WindowAccentCompositor(this);
&lt;span class=&quot;gd&quot;&gt;--  compositor.Composite(Color.FromRgb(0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  compositor.Composite(Color.FromArgb(0x00, 0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-40-35.png&quot; alt=&quot;no acrylic without WindowChrome&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;接下来是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 时：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16 48 16 16&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-47-52.png&quot; alt=&quot;acrylic with WindowChrome frame&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而周围有一圈偏白色的渐变是什么呢？那个其实是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 设置的边框白，被亚克力效果模糊后得到的混合效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;所以，如果要获得全窗口的亚克力效果，请将边框设置成比较小的值：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 1 0 0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-50-34.png&quot; alt=&quot;acrylic with thin WindowChrome frame&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;记得不要像前面的那些效果一样，如果设置成 &lt;code class=&quot;highlighter-rouge&quot;&gt;-1&lt;/code&gt;，你将获得纯白色与设置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Gradient&lt;/code&gt; 叠加色的亚克力特效，是个纯色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-52-07.png&quot; alt=&quot;acrylic with WindowChrome -1 frame&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;你可以将叠加色的透明度设置得小一些，这样可以看出叠加的颜色：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    var compositor = new WindowAccentCompositor(this);
&lt;span class=&quot;gd&quot;&gt;--  compositor.Composite(Color.FromRgb(0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  compositor.Composite(Color.FromArgb(0xa0, 0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-53-56.png&quot; alt=&quot;acrylic with darker gradient color&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;那么可以设置为全透明吗？&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    var compositor = new WindowAccentCompositor(this);
&lt;span class=&quot;gd&quot;&gt;--  compositor.Composite(Color.FromRgb(0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  compositor.Composite(Color.FromArgb(0x00, 0x18, 0xa0, 0x5e));
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很不幸，最终你会完全看不到亚克力效果，而变成了毫无特效的透明窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-21-58-40.png&quot; alt=&quot;acrylic with transparent gradient color&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最上面那根白线，是我面前面设置边框为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0 1 0 0&lt;/code&gt; 导致的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果在这种情况下，将边框设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 会怎样呢？记得前面我们说过的吗，会导致阴影消失哦！&lt;/p&gt;

&lt;p&gt;呃……你将看到……这个……&lt;/p&gt;

&lt;p&gt;什么都没有……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-09-22-02-48.png&quot; alt=&quot;acrylic with zero WindowChrome frame thickness&quot; /&gt;&lt;/p&gt;

&lt;p&gt;是不是找到了一条新的背景透明异形窗口的方法？&lt;/p&gt;

&lt;p&gt;还是省点心吧，亚克力效果在 Win32 应用上的性能还是比较堪忧的……&lt;/p&gt;

&lt;p&gt;想要背景透明，请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-transparent-window-without-allows-transparency&quot;&gt;WPF 制作高性能的透明背景异形窗口（使用 WindowChrome 而不要使用 AllowsTransparency=True） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;不用考虑 Windows 7，因为大家都知道不支持。实际效果会跟前面的一模一样。&lt;/p&gt;

&lt;h3 id=&quot;accentstateaccent_invalid_state&quot;&gt;AccentState=ACCENT_INVALID_STATE&lt;/h3&gt;

&lt;p&gt;这个值其实不用说了，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AccentState&lt;/code&gt; 在不同系统中可用的值不同，为了保证向后兼容性，对于新系统中设置的值，旧系统其实就视之为 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_INVALID_STATE&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;那么如果系统认为设置的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_INVALID_STATE&lt;/code&gt; 会显示成什么样子呢？&lt;/p&gt;

&lt;p&gt;答案是，与 &lt;code class=&quot;highlighter-rouge&quot;&gt;ACCENT_DISABLED&lt;/code&gt; 完全相同。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;由于 Windows 7 上所有的值都是同样的效果，所以下表仅适用于 Windows 10。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;效果&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_DISABLED&lt;/td&gt;
      &lt;td&gt;黑色（边框为纯白色）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_ENABLE_GRADIENT&lt;/td&gt;
      &lt;td&gt;GradientColor 颜色（失焦后边框为深色）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_ENABLE_TRANSPARENTGRADIENT&lt;/td&gt;
      &lt;td&gt;主题色（失焦后边框为深色）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_ENABLE_BLURBEHIND&lt;/td&gt;
      &lt;td&gt;模糊特效（失焦后边框为灰色）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/td&gt;
      &lt;td&gt;与 GradientColor 叠加颜色的亚克力特效&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ACCENT_INVALID_STATE&lt;/td&gt;
      &lt;td&gt;黑色（边框为纯白色）&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在以上的特效之下，&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 可以让客户区覆盖非客户区，或者让整个窗口都获得特效，而不只是标题栏。&lt;/p&gt;

&lt;h2 id=&quot;附源代码&quot;&gt;附源代码&lt;/h2&gt;

&lt;p&gt;请参见 GitHub 地址以获得最新代码。如果不方便访问，那么就看下面的吧。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/blob/master/src/Themes/Walterlv.Themes.FluentDesign/Effects/WindowAccentCompositor.cs&quot;&gt;Walterlv.Packages/WindowAccentCompositor.cs at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows.Effects&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 为窗口提供模糊特效。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowAccentCompositor&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 创建 &amp;lt;see cref=&quot;WindowAccentCompositor&quot;/&amp;gt; 的一个新实例。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;window&quot;&amp;gt;要创建模糊特效的窗口实例。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowAccentCompositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnsureHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gradientColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 组装红色分量。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 组装绿色分量。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 组装蓝色分量。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 组装透明分量。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;Composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gradientColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 创建 AccentPolicy 对象。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentPolicy&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;GradientColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 将托管结构转换为非托管对象。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accentPolicySize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AllocHGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accentPolicySize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructureToPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accentPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 设置窗口组合特性。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 设置模糊特效。&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowCompositionAttributeData&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowCompositionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WCA_ACCENT_POLICY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SizeOfData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accentPolicySize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accentPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetWindowCompositionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 释放非托管对象。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FreeHGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accentPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowCompositionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowCompositionAttributeData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_DISABLED&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_GRADIENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_TRANSPARENTGRADIENT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_BLURBEHIND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_ENABLE_ACRYLICBLURBEHIND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ACCENT_INVALID_STATE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccentPolicy&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccentFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GradientColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnimationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WindowCompositionAttributeData&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowCompositionAttribute&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SizeOfData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowCompositionAttribute&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略其他未使用的字段&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;WCA_ACCENT_POLICY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略其他未使用的字段&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 10 Oct 2019 00:09:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/set-window-composition-attribute.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/set-window-composition-attribute.html</guid>
        
        
        <category>windows</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用傲梅分区助手无损合并分区，无损调整分区大小</title>
        <description>&lt;p&gt;Windows 本身就提供了强大的磁盘和分区管理工具，一个是操作简单的“磁盘管理”，一个是功能强大的命令行版的“diskpart”。不过这两个都有一些限制，一是不能影响到系统文件，二是其修改的分区不能被应用程序占用（diskpart 可在下次重启时做到）。另外，系统为了管理工具操作的效率和正确性，也有一些功能没有开放。&lt;/p&gt;

&lt;p&gt;DiskGenius 是个强大的工具，不过傲梅也很良心。本文介绍使用傲梅分区助手来管理磁盘。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载&quot;&gt;下载&lt;/h2&gt;

&lt;p&gt;傲梅分区助手有绿色版、专业版和 PE 版。一般我们选择绿色版就好，如果你要改到系统分区，就需要使用集成了傲梅分区助手的 PE 系统。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.disktool.cn/download.html&quot;&gt;下载傲梅分区助手&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下面是专业版的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-19-53-10.png&quot; alt=&quot;专业版&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面是 PE 版的截图，也是我实际操作分区时截下来的图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-19-54-35.png&quot; alt=&quot;PE 版&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不要吐槽为何我用的是古老的 1709 系统，实际上我的系统盘是下面那个 I 盘。不然为什么我会把系统的版本号放到卷标中呢？&lt;/p&gt;

&lt;h2 id=&quot;调整分区大小&quot;&gt;调整分区大小&lt;/h2&gt;

&lt;p&gt;在 PE 系统中找到傲梅分区助手，然后启动。在需要调整位置和大小的分区上右键点击选择“调整/移动分区”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-19-57-11.png&quot; alt=&quot;调整移动分区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在弹出的详细设置对话框中调整分区的位置和大小。如果是 SSD，建议点击“高级”然后勾选“允许分区对齐以优化SSD或HDD硬盘”，这可以开启 4K 对齐以大幅优化 SSD 的读写性能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-19-58-46.png&quot; alt=&quot;4K 对齐&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后点击确定。&lt;/p&gt;

&lt;p&gt;注意这个时候还没有开始执行真正的操作！&lt;/p&gt;

&lt;h2 id=&quot;合并分区&quot;&gt;合并分区&lt;/h2&gt;

&lt;p&gt;合并分区功能可以将你一个磁盘中的多个分区无损合并成一个。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-20-11-15.png&quot; alt=&quot;合并分区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选择好将哪个分区合并到哪一个，这时另一个分区中的所有文件会放到目标分区中的一个文件夹里。合并完之后你自己移动好这些文件即可。&lt;/p&gt;

&lt;p&gt;因为我的分区在合并过程中的操作没有截图，所以只能看到下面这个提前在磁盘管理中的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-02-13.png&quot; alt=&quot;无法删除&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;开始执行真正的操作&quot;&gt;开始执行真正的操作&lt;/h2&gt;

&lt;p&gt;在你设置好你的所有操作之后，点击左上角的“提交”按钮，这可以开始依次执行之前所有设置的磁盘最终状态。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-20-03-24.png&quot; alt=&quot;提交&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在提交界面，你可以看到即将进行的所有操作的简介，以及预计完成这些操作所花的时间。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-20-05-48.png&quot; alt=&quot;操作预览&quot; /&gt;&lt;/p&gt;

&lt;p&gt;虽然上图只是示例，但我实际将我在下面这篇博客中删除出来的空余空间全部合并在一起，并且还额外合并了两个都需要保留数据的分区。这个过程傲梅的预计时间是 9小时18分，实际上也刚好在 9 个小时左右！&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-2&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（全命令行操作）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以，如果你打算开始进行大量的磁盘调整、对拷或者其他&lt;strong&gt;无损分区&lt;/strong&gt;操作：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;请提前准备好大量你不用电脑的时间。&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;请提前准备好大量你不用电脑的时间。&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;请提前准备好大量你不用电脑的时间。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是我实际上在 PE 中操作的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-20-08-33.png&quot; alt=&quot;分区调整过程&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Oct 2019 12:14:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/expand-or-merge-partition-using-aomei.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/expand-or-merge-partition-using-aomei.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>EFI 分区/恢复分区不可删除？你需要使用命令行了（配合鼠标操作）</title>
        <description>&lt;p&gt;Windows 系统在安装的时候，会自动为我们的磁盘划分一个恢复分区和一个 EFI 分区。如果后面不打算再用这些分区的时候，却发现无法删除。&lt;/p&gt;

&lt;p&gt;本文将提供解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;因为误操作会导致数据丢失，所以我将两种不同的解决方法分开成两篇文章以避免干扰：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-1&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（配合鼠标操作）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-2&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（全命令行操作）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;无法删除&quot;&gt;无法删除&lt;/h2&gt;

&lt;p&gt;看下图，有两种不同类型的无法删除：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有完整菜单只是删除按钮不可用的 EFI 分区；&lt;/li&gt;
  &lt;li&gt;仅有一个“帮助”菜单的恢复分区。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;删除方法会略有不同，我会在合适的地方提示你使用正确的方法的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-02-13.png&quot; alt=&quot;无法删除&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我的磁盘 2 原本包含两个可见分区，一个是图中黑色色块，原来放的是旧操作系统，一个是图中的 D 盘，放大量文件。因为我新买了一个大容量 SSD 专门用来放操作系统，所以原来操作系统所在的磁盘就可以回收与 D 盘合并。&lt;/p&gt;

&lt;p&gt;然而悲剧的是，中间隔着一个 820MB 的恢复分区，导致我没有办法为 D 分区扩容。&lt;/p&gt;

&lt;p&gt;更麻烦的是，在磁盘管理中，这三个我不会再使用的恢复分区都不可删除。&lt;/p&gt;

&lt;p&gt;PS. 吐槽一下，大版本升级一次 Windows 10 竟然会在后面给我多创建一个恢复分区……&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;在实操之前，你必须清除地知道你每一步在做什么，否则你需要承担丢失数据的后果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你在网上找到一些操作 disk 的命令，请不要相信——&lt;strong&gt;因为此命令清除的是整个磁盘而不只是单个分区&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;第一步打开命令提示符&quot;&gt;第一步：打开命令提示符&lt;/h3&gt;

&lt;p&gt;打开开始菜单，输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 然后回车确定，我们可以打开命令提示符&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-11-03.png&quot; alt=&quot;cmd&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第二步打开-diskpart&quot;&gt;第二步：打开 diskpart&lt;/h3&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;diskpart&lt;/code&gt; 然后回车，你会看到一个 UAC 提示弹窗，点击“是”之后会启动一个新的管理员权限启动的命令提示符，这里运行着 Diskpart 程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-14-07.png&quot; alt=&quot;diskpart&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第三步找到要操作的磁盘&quot;&gt;第三步：找到要操作的磁盘&lt;/h3&gt;

&lt;p&gt;输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;list disk&lt;/code&gt; 回车，我们可以看到自己计算机中所有已经插入的磁盘。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###  状态           大小     可用     Dyn  Gpt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;931&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;489&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;199&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;476&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请注意，这里看到的是磁盘，而不是平时在“计算机”中看到的分区——每一个磁盘都可以包含一个到多个分区哦！&lt;/p&gt;

&lt;p&gt;你有两种方法来确认我们即将操作的是哪个磁盘：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前面我们在磁盘管理中看到的那个界面，也就是本文一开始的那张图，可以直接看出我们要删除的分区在“磁盘 2”上。&lt;/li&gt;
  &lt;li&gt;根据分区的大小去猜，相信分区少的时候你可以猜对。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;好的，我们知道要操作的是“磁盘 2”，于是我们输入命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;select disk 2&lt;/code&gt;（如果你是其他磁盘请换成自己的数字）：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;现在是所选磁盘。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;紧接着，我们输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;list partition&lt;/code&gt; 列出此磁盘上的所有分区：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###       类型              大小     偏移量&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;恢复&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;499&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;系统&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;保留&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;恢复&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;820&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;199&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;主要&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;288&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过分区，我们也能再次确认我们找到了正确的要操作的磁盘。&lt;/p&gt;

&lt;p&gt;截至目前，我们还没有对系统进行任何更改，所以你操作错了也不用担心。但接下来你就需要谨慎一些。&lt;/p&gt;

&lt;h3 id=&quot;第-41-步删除分区仅适用于-efi-分区&quot;&gt;第 4.1 步：删除分区（仅适用于 EFI 分区）&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-39-34.png&quot; alt=&quot;EFI 分区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为我不再将此磁盘用作系统盘，所以里面除了那个 288GB 的数据部分不能动之外，其他系统生成的部分都是需要删除的，所以接下来我需要对分区 1 2 5 都进行一遍以下操作（你的目的不同可能需要删除的分区也不一样）。&lt;/p&gt;

&lt;p&gt;先选中要操作的分区：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;现在是所选分区。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后更改其 ID：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ebd0a0a2-b9e5-4433-87c0-68b6b72699c7&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiskPart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;成功设置了分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后操作其他的分区。&lt;/p&gt;

&lt;p&gt;完整的截图如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-30-17.png&quot; alt=&quot;完整的截图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个时候，回到磁盘管理中，F5 刷新，你可以看到原本不可删除的 EFI 分区，现在可以直接使用鼠标删除了。点击一下“删除卷”即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-50-47.png&quot; alt=&quot;在磁盘管理中删除卷&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第-42-步删除分区适用于所有类型的分区&quot;&gt;第 4.2 步：删除分区（适用于所有类型的分区）&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-39-46.png&quot; alt=&quot;恢复分区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;恢复分区不能使用上面 4.1 中的方法删除，如果你在 4.1 的操作之后还发现存在不可删除的恢复分区，请尝试使用我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-2&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（全命令行操作）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/sinat_29957455/article/details/88726797&quot;&gt;windows10删除EFI分区(绝对安全) - 修炼之路 - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.easeus.com/partition-master/delete-efi-system-partition.html&quot;&gt;How to Delete EFI System Partition in Windows 10/8.1/8/7/XP/Vista - EaseUS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 07 Oct 2019 03:05:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/delete-efi-partition-that-cannot-be-deleted-1.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/delete-efi-partition-that-cannot-be-deleted-1.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>EFI 分区/恢复分区不可删除？你需要使用命令行了（全命令行操作）</title>
        <description>&lt;p&gt;Windows 系统在安装的时候，会自动为我们的磁盘划分一个恢复分区和一个 EFI 分区。如果后面不打算再用这些分区的时候，却发现无法删除。&lt;/p&gt;

&lt;p&gt;本文将提供解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;因为误操作会导致数据丢失，所以我将两种不同的解决方法分开成两篇文章以避免干扰：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-1&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（配合鼠标操作）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/delete-efi-partition-that-cannot-be-deleted-2&quot;&gt;EFI 分区/恢复分区不可删除？你需要使用命令行了（全命令行操作）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;无法删除&quot;&gt;无法删除&lt;/h2&gt;

&lt;p&gt;看下图，有两种不同类型的无法删除：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有完整菜单只是删除按钮不可用的 EFI 分区；&lt;/li&gt;
  &lt;li&gt;仅有一个“帮助”菜单的恢复分区。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;使用本文提供的方法，你可以删除以上两种不同类型的分区。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-02-13.png&quot; alt=&quot;无法删除&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我的磁盘 2 原本包含两个可见分区，一个是图中黑色色块，原来放的是旧操作系统，一个是图中的 D 盘，放大量文件。因为我新买了一个大容量 SSD 专门用来放操作系统，所以原来操作系统所在的磁盘就可以回收与 D 盘合并。&lt;/p&gt;

&lt;p&gt;然而悲剧的是，中间隔着一个 820MB 的恢复分区，导致我没有办法为 D 分区扩容。&lt;/p&gt;

&lt;p&gt;更麻烦的是，在磁盘管理中，这三个我不会再使用的恢复分区都不可删除。&lt;/p&gt;

&lt;p&gt;PS. 吐槽一下，大版本升级一次 Windows 10 竟然会在后面给我多创建一个恢复分区……&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;在实操之前，你必须清除地知道你每一步在做什么，否则你需要承担丢失数据的后果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你在网上找到一些操作 disk 的命令，请不要相信——&lt;strong&gt;因为此命令清除的是整个磁盘而不只是单个分区&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;第一步打开命令提示符&quot;&gt;第一步：打开命令提示符&lt;/h3&gt;

&lt;p&gt;打开开始菜单，输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 然后回车确定，我们可以打开命令提示符&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-11-03.png&quot; alt=&quot;cmd&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第二步打开-diskpart&quot;&gt;第二步：打开 diskpart&lt;/h3&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;diskpart&lt;/code&gt; 然后回车，你会看到一个 UAC 提示弹窗，点击“是”之后会启动一个新的管理员权限启动的命令提示符，这里运行着 Diskpart 程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-14-07.png&quot; alt=&quot;diskpart&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第三步找到要操作的磁盘&quot;&gt;第三步：找到要操作的磁盘&lt;/h3&gt;

&lt;p&gt;输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;list disk&lt;/code&gt; 回车，我们可以看到自己计算机中所有已经插入的磁盘。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###  状态           大小     可用     Dyn  Gpt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;238&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;931&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;489&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;199&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;476&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请注意，这里看到的是磁盘，而不是平时在“计算机”中看到的分区——每一个磁盘都可以包含一个到多个分区哦！&lt;/p&gt;

&lt;p&gt;你有两种方法来确认我们即将操作的是哪个磁盘：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前面我们在磁盘管理中看到的那个界面，也就是本文一开始的那张图，可以直接看出我们要删除的分区在“磁盘 2”上。&lt;/li&gt;
  &lt;li&gt;根据分区的大小去猜，相信分区少的时候你可以猜对。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;好的，我们知道要操作的是“磁盘 2”，于是我们输入命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;select disk 2&lt;/code&gt;（如果你是其他磁盘请换成自己的数字）：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;现在是所选磁盘。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;紧接着，我们输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;list partition&lt;/code&gt; 列出此磁盘上的所有分区：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###       类型              大小     偏移量&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;恢复&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;499&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;系统&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;保留&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;恢复&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;820&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;199&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;主要&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;288&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过分区，我们也能再次确认我们找到了正确的要操作的磁盘。&lt;/p&gt;

&lt;p&gt;截至目前，我们还没有对系统进行任何更改，所以你操作错了也不用担心。但接下来你就需要谨慎一些。&lt;/p&gt;

&lt;h3 id=&quot;第四步删除分区&quot;&gt;第四步：删除分区&lt;/h3&gt;

&lt;p&gt;因为我不再将此磁盘用作系统盘，所以里面除了那个 288GB 的数据部分不能动之外，其他系统生成的部分都是需要删除的，所以接下来我需要对分区 1 2 5 都进行一遍以下操作（你的目的不同可能需要删除的分区也不一样）。&lt;/p&gt;

&lt;p&gt;先选中要操作的分区：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;现在是所选分区。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;delete part override&lt;/code&gt; 删除这个分区：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;override&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiskPart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;成功地删除了所选分区。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接着，依次删除其他分区。下面是删除其中前两个分区后的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-10-58-28.png&quot; alt=&quot;删除分区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所有分区删除完毕之后，可以看到我的整个磁盘现在只剩下我要留下的重要数据分区了。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###       类型              大小     偏移量&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;分区&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;主要&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;288&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时回到磁盘管理中，可以看到大量已被删除的未分配的空间连在了一起。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-10-07-11-01-51.png&quot; alt=&quot;未分配&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以将我的 D 盘扩展更多空间啦！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/36238673&quot;&gt;磁盘中出现多个的恢复分区，能否删除？ - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 07 Oct 2019 03:04:58 +0000</pubDate>
        <link>https://blog.walterlv.com/post/delete-efi-partition-that-cannot-be-deleted-2.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/delete-efi-partition-that-cannot-be-deleted-2.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>推荐几款连字字体，在代码编辑器中启用连字字体（Visual Studio Code）</title>
        <description>&lt;p&gt;启用转为编程设计的连字字体，可以给你的变成带来不一样的体验。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;连字字体&quot;&gt;连字字体&lt;/h2&gt;

&lt;p&gt;微软随 &lt;a href=&quot;https://github.com/microsoft/terminal&quot;&gt;Windows Terminal&lt;/a&gt; 设计了一款新的字体 Cascadia Code，而这是一款连字字体。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/cascadia-code&quot;&gt;microsoft/cascadia-code: This is a fun, new monospaced font that includes programming ligatures and is designed to enhance the modern look and feel of the Windows Terminal.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你可以看到，在 Windows Terminal 的终端中，&lt;code class=&quot;highlighter-rouge&quot;&gt;=&amp;gt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt; 符号显示成了更容易理解的连字符号：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-27-10-01-28.png&quot; alt=&quot;Cascadia Code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Cascadia Code 发布之前，Fira Code 是一款特别火的连字字体，下面是 Fira Code 连字字体在 Visual Studio Code 中的显示效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-30-08-30-37.png&quot; alt=&quot;Fira Code in Visual Studio Code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而显示的，其实是下面这一段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &amp;gt;=&amp;gt; 欢迎访问吕毅的博客 ~~&amp;gt; blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;连字字体推荐&quot;&gt;连字字体推荐&lt;/h2&gt;

&lt;p&gt;作为微软的粉丝，当然首推 &lt;a href=&quot;https://github.com/microsoft/cascadia-code&quot;&gt;Cascadia Code&lt;/a&gt;！不过我喜欢比较细的字体风格，目前 Cascadia Code 还没有提供细体，因此我可能还需要等一些时间才正式入坑。&lt;/p&gt;

&lt;p&gt;在这里可以关注 Cascadia Code 的状态：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/cascadia-code&quot;&gt;microsoft/cascadia-code: This is a fun, new monospaced font that includes programming ligatures and is designed to enhance the modern look and feel of the Windows Terminal.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;灵台，你也可以在这里找到其他一些好看的用于编程的连字字体：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.slant.co/topics/5611/~monospace-programming-fonts-with-ligatures#2&quot;&gt;8 Best monospace programming fonts with ligatures as of 2019 - Slant&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;相关的开源项目链接：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/FiraCode&quot;&gt;tonsky/FiraCode: Monospaced font with programming ligatures&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/i-tu/Hasklig&quot;&gt;i-tu/Hasklig: Hasklig - a code font with monospaced ligatures&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/be5invis/Iosevka&quot;&gt;be5invis/Iosevka: Slender typeface for code, from code.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubjo.github.io/victor-mono/&quot;&gt;Victor Mono&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以 Fira Code 为例安装的话，去它的 GitHub 的 release 页面：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/FiraCode/releases&quot;&gt;Releases · tonsky/FiraCode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载最新的发布文件 &lt;a href=&quot;https://github.com/tonsky/FiraCode/releases/download/1.207/FiraCode_1.207.zip&quot;&gt;FiraCode_1.207.zip&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;下载解压后，你会看到五个不同的文件夹，这是四种不同的字体类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;otf (Open Type)&lt;/li&gt;
  &lt;li&gt;ttf (True Type)&lt;/li&gt;
  &lt;li&gt;variable_ttf (Variable True Type)&lt;/li&gt;
  &lt;li&gt;woff (Web Open Font Format)&lt;/li&gt;
  &lt;li&gt;woff2 (Web Open Font Format)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于 Open Type 和 True Type 的选择，一般有对应的 Open Type 类型字体的时候就优先选择 Open Type 类型的，因为 True Type 格式是比较早期的，限制比较多，比如字符的数量受到限制，而 Open Type 是基于 Unicode 字符集来设计的新的跨平台的字体格式。&lt;/p&gt;

&lt;p&gt;Variable True Type 是可以无极变换的 True Type 字体。&lt;/p&gt;

&lt;p&gt;而 Web Open Font Format 主要为网络传输优化，其特点是字体均经过压缩，其大小会比较小。&lt;/p&gt;

&lt;p&gt;我们点击进入 &lt;code class=&quot;highlighter-rouge&quot;&gt;otf&lt;/code&gt; 文件夹，然后全选所有的字体文件，右键，安装，等待安装完成即可。&lt;/p&gt;

&lt;h2 id=&quot;在编辑器中启用&quot;&gt;在编辑器中启用&lt;/h2&gt;

&lt;h3 id=&quot;在-visual-studio-code-中启用&quot;&gt;在 Visual Studio Code 中启用&lt;/h3&gt;

&lt;p&gt;在 Visual Studio Code 中启用连字字体需要用到两个选项：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.fontFamily&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Fira Code Light, Consolas, Microsoft YaHei&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.fontLigatures&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-31-08-48-38.png&quot; alt=&quot;打开 Visual Studio Code 设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后点击新打开的标签右上角的 &lt;code class=&quot;highlighter-rouge&quot;&gt;{}&lt;/code&gt; 图标以打开 json 形式编辑的设置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-31-08-49-46.png&quot; alt=&quot;使用 json 编辑设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后修改把上面两个设置增加或替换进去即可。下面是我的设置的部分截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-31-08-50-30.png&quot; alt=&quot;设置启用连字字体&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在-visual-studio-或其他-windows-系统自带软件中启用&quot;&gt;在 Visual Studio 或其他 Windows 系统自带软件中启用&lt;/h3&gt;

&lt;p&gt;只需要将字体设置成 Fira Code 即可。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://thetype.com/2016/09/10968/&quot;&gt;Type is Beautiful » 参数化设计与字体战争：从 OpenType 1.8 说起&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 27 Sep 2019 02:04:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-font-ligatures-for-coding.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-font-ligatures-for-coding.html</guid>
        
        
        <category>windows</category>
        
        <category>vscode</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何在 Visual Studio 2019 中设置使用 .NET Core SDK 的预览版（全局生效）</title>
        <description>&lt;p&gt;.NET Core 3 相比于 .NET Core 2 是一个大更新。也正因为如此，即便它长时间处于预览版尚未发布的状态，大家也一直在使用。&lt;/p&gt;

&lt;p&gt;Visual Studio 2019 中提供了使用 .NET Core SDK 预览版的开关。但几个更新的版本其开关的位置不同，本文将介绍在各个版本中的位置，方便你找到然后设置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;visual-studio-2019-163-及以上&quot;&gt;Visual Studio 2019 (16.3 及以上)&lt;/h2&gt;

&lt;p&gt;.NET Core 3.0 已经发布，下载地址：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;Download .NET (Linux, macOS, and Windows)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visual Studio 16.3 与 .NET Core 3.0 正式版同步发布，因此不再需要 .NET Core 3.0 的预览版设置界面。你只需要安装正式版 .NET Core SDK 即可。&lt;/p&gt;

&lt;h2 id=&quot;visual-studio-2019-162&quot;&gt;Visual Studio 2019 (16.2)&lt;/h2&gt;

&lt;p&gt;从 Visual Studio 2019 的 16.2 版本，.NET Core 预览版的设置项的位置在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;环境&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;预览功能&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Use previews of the .NET Core SDK (需要 restart)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-18-31-15.png&quot; alt=&quot;Visual Studio 2019 16.2 的设置位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你是英文版的 Visual Studio，也可以参考英文版：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Environment&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Preview Features&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Use previews of the .NET Core SDK (requires restart)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-18-34-43.png&quot; alt=&quot;Option location of Visual Studio 2019 16.2&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;visual-studio-2019-161&quot;&gt;Visual Studio 2019 (16.1)&lt;/h2&gt;

&lt;p&gt;从 Visual Studio 2019 的 16.1 版本，.NET Core 预览版的设置项的位置在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;环境&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;预览功能&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;使用 .NET Core SDK 的预览&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-00-09.png&quot; alt=&quot;Visual Studio 2019 16.1 的设置位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你是英文版的 Visual Studio，也可以参考英文版：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Environment&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Preview Features&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Use previews of the .NET Core SDK&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-11-48.png&quot; alt=&quot;Option location of Visual Studio 2019 16.1&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;visual-studio-2019-160-和早期预览版&quot;&gt;Visual Studio 2019 (16.0 和早期预览版)&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 2019 的早期，.NET Core 在设置中是有一个专用的选项的，在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;项目和解决方案&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;.NET Core&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;使用 .NET Core SDK 预览版&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-08-23.png&quot; alt=&quot;Visual Studio 2019 16.0 的设置位置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你是英文版的 Visual Studio，也可以参考英文版：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Projects and solutions&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;.NET Core&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Use previews of the .NET Core SDK&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-23-36.png&quot; alt=&quot;Option location of Visual Studio 2019 16.0&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;关于全局配置&quot;&gt;关于全局配置&lt;/h2&gt;

&lt;p&gt;Visual Studio 2019 中此对于 .NET Core SDK 的预览版的设置是全局生效的。&lt;/p&gt;

&lt;p&gt;也就是说，你在 Visual Studio 2019 中进行了此设置，在命令行中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令进行编译也会使用这样的设置项。&lt;/p&gt;

&lt;p&gt;那么这个全局的设置项在哪个地方呢？是如何全局生效的呢？可以阅读我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/where-is-the-dotnet-sdk-preview-config-file&quot;&gt;Visual Studio 2019 中使用 .NET Core 预览版 SDK 的全局配置文件在哪里？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/find-out-the-dotnet-sdk-preview-config-file&quot;&gt;找出 .NET Core SDK 是否使用预览版的全局配置文件在那里（探索篇）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 24 Sep 2019 00:37:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-set-dotnet-core-sdk-preview-in-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-set-dotnet-core-sdk-preview-in-visual-studio.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 程序如何移动焦点到其他控件</title>
        <description>&lt;p&gt;WPF 中可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement.Focus()&lt;/code&gt; 将焦点设置到某个特定的控件，也可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TraversalRequest&lt;/code&gt; 仅仅移动焦点。本文介绍如何在 WPF 程序中控制控件的焦点。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;uielementfocus&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement.Focus&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;仅仅需要在任何一个控件上调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Focus()&lt;/code&gt; 方法即可将焦点设置到这个控件上。&lt;/p&gt;

&lt;p&gt;但是需要注意，要使 &lt;code class=&quot;highlighter-rouge&quot;&gt;Focus()&lt;/code&gt; 能够工作，这个元素必须满足两个条件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Focusable&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IsVisible&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;traversalrequest&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TraversalRequest&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;如果你并不是将焦点设置到某个特定的控件，而是希望将焦点转移，可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TraversalRequest&lt;/code&gt; 类。&lt;/p&gt;

&lt;p&gt;比如，以下代码是将焦点转移到下一个控件，也就是按下 Tab 键时焦点会去的控件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traversalRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TraversalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FocusNavigationDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// view 是可视化树中的一个控件。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MoveFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traversalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于逻辑焦点和键盘焦点&quot;&gt;关于逻辑焦点和键盘焦点&lt;/h2&gt;

&lt;p&gt;键盘焦点就是你实际上按键输入和快捷键会生效的焦点，也就是当前正在工作的控件的焦点。&lt;/p&gt;

&lt;p&gt;而 WPF 有多个焦点范围（Focus Scope），按下 Tab 键切换焦点的时候只会在当前焦点范围切焦点，不会跨范围。那么一旦跨范围切焦点的时候，焦点会去哪里呢？答案是逻辑焦点。&lt;/p&gt;

&lt;p&gt;每个焦点范围内都有一个逻辑焦点，记录如果这个焦点范围一旦获得焦点后应该在哪个控件获得键盘焦点。&lt;/p&gt;

&lt;p&gt;比如默认情况下 WPF 每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 就是一个焦点范围，那么每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 中的当前焦点就是逻辑焦点。而一旦这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 激活，那么这个窗口中的逻辑焦点就会成为键盘焦点，另一个窗口当中的逻辑焦点保留，而键盘焦点则丢失。&lt;/p&gt;

&lt;h2 id=&quot;跨窗口跨进程切换焦点&quot;&gt;跨窗口/跨进程切换焦点&lt;/h2&gt;

&lt;p&gt;参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/move-focus-to-win32-window&quot;&gt;WPF 程序如何跨窗口/跨进程设置控件焦点&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/31570683/6233938&quot;&gt;winapi - Win32: C++: How do I re-focus on Parent Window after clicking in a child window? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 19 Sep 2019 03:41:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-move-focus.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-move-focus.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 SetParent 制作父子窗口的时候，如何设置子窗口的窗口样式以避免抢走父窗口的焦点</title>
        <description>&lt;p&gt;制作传统 Win32 程序以及 Windows Forms 程序的时候，一个用户看起来独立的窗口本就是通过各种父子窗口嵌套完成的，有大量窗口句柄，窗口之间形成父子关系。不过，对于 WPF 程序来说，一个独立的窗口实际上只有一个窗口句柄，窗口内的所有内容都是 WPF 绘制的。&lt;/p&gt;

&lt;p&gt;如果你不熟悉 Win32 窗口中的父子窗口关系和窗口样式，那么很有可能遇到父子窗口之间“抢夺焦点”的问题，本文介绍如何解决这样的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;抢夺焦点&quot;&gt;“抢夺焦点”&lt;/h2&gt;

&lt;p&gt;下图中的上下两个部分是两个不同的窗口，他们之间通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 建立了父子关系。&lt;/p&gt;

&lt;p&gt;注意看下面的窗口标题栏，当我在这些不同区域间点击的时候，窗口标题栏在黑色和灰色之间切换：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-19-activation-between-parent-child-windows.gif&quot; alt=&quot;抢夺焦点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这说明当子窗口获得焦点的时候，父窗口会失去焦点并显示失去焦点的样式。&lt;/p&gt;

&lt;p&gt;你可以在&lt;a href=&quot;/post/hosted-hwnd-must-be-a-child-window&quot;&gt;这篇博客&lt;/a&gt;中找到一个简单的例子：&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;而原因和解决方法仅有一个，就是子窗口需要有一个子窗口的样式。&lt;/p&gt;

&lt;p&gt;具体来说，子窗口必须要有 &lt;code class=&quot;highlighter-rouge&quot;&gt;WS_CHILD&lt;/code&gt; 样式。&lt;/p&gt;

&lt;p&gt;你可以看看 Spyxx.exe 抓出来的默认普通窗口和子窗口的样式差别：&lt;/p&gt;

&lt;p&gt;![默认普通窗口]](/static/posts/2019-09-19-10-21-31.png)&lt;/p&gt;

&lt;p&gt;▲ 默认普通窗口&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-19-10-21-47.png&quot; alt=&quot;子窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;▲ 子窗口&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/helloj2ee/archive/2009/05/29/1491822.html&quot;&gt;关于WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解（个人认为还是相当全面的） - helloj2ee - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 19 Sep 2019 02:24:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win32-child-window-style.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win32-child-window-style.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件</title>
        <description>&lt;p&gt;弱引用是 .NET 引入的概念，可以用来协助解决内存泄漏问题。然而事件也可能带来内存泄漏问题，是否有弱事件机制可以使用呢？.NET 没有自带的弱事件机制，但其中的一个子集 WPF 带了。然而我们不是什么项目都能引用 WPF 框架类库的。&lt;/p&gt;

&lt;p&gt;本文介绍 Walterlv.WeakEvents 库来定义和使用弱事件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event-relay&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-dotnet-weak-event-relay&quot;&gt;.NET 设计一套高性能的弱事件机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;下载安装-walterlvweakevents&quot;&gt;下载安装 Walterlv.WeakEvents&lt;/h2&gt;

&lt;p&gt;在你需要做弱事件的项目中安装 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.WeakEvents/&quot;&gt;Walterlv.WeakEvents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;定义弱事件&quot;&gt;定义弱事件&lt;/h2&gt;

&lt;p&gt;现在，定义弱事件就不能直接写 &lt;code class=&quot;highlighter-rouge&quot;&gt;event EventHandler Bar&lt;/code&gt; 了，要像下面这样写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WeakEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用弱事件&quot;&gt;使用弱事件&lt;/h2&gt;

&lt;p&gt;对于弱事件的使用，就跟以前任何其他正常事件一样了，直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这样，如果我有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; 类的实例 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;，订阅了以上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 事件，那么当 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 脱离作用范围后，将可以被垃圾回收机制回收。而如果不这么做，&lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 将始终保留对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 实例的引用，这将阻止垃圾回收。&lt;/p&gt;
</description>
        <pubDate>Wed, 18 Sep 2019 14:05:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/implement-custom-dotnet-weak-event.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/implement-custom-dotnet-weak-event.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 设计一套高性能的弱事件机制</title>
        <description>&lt;p&gt;弱引用是 .NET 引入的概念，可以用来协助解决内存泄漏问题。然而事件也可能带来内存泄漏问题，是否有弱事件机制可以使用呢？.NET 没有自带的弱事件机制，但其中的一个子集 WPF 带了。然而我们不是什么项目都能引用 WPF 框架类库的。网上有很多弱事件的 NuGet 包，不过仅仅支持定义事件的时候写成弱事件而不支持让任意事件变成弱事件，并且存在性能问题。&lt;/p&gt;

&lt;p&gt;本文将设计一套弱事件机制，不止可以让任意一个 CLR 事件成为弱事件，还具有近乎原生事件的性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event-relay&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-dotnet-weak-event-relay&quot;&gt;.NET 设计一套高性能的弱事件机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;场景与问题&quot;&gt;场景与问题&lt;/h2&gt;

&lt;p&gt;本文主要为了设计一套弱事件机制而编写，因此如果你感兴趣，应该已经理解了我试图做什么事情。&lt;/p&gt;

&lt;p&gt;当然，如果并不理解，可以阅读这个机制的应用篇，里面有具体的应用场景：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event-relay&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;现有设计&quot;&gt;现有设计&lt;/h2&gt;

&lt;p&gt;在我进行此设计之前，已有如下种类的弱事件机制：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;WPF 框架自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventManager&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;功能非常有限，自己继承实现一个的难度非常高，但具有很高的性能；WPF 绑定等机制的底层实现用到了这个类型。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;WPF 框架自带的泛型类 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventManager&amp;lt;TEventSource, TEventArgs&amp;gt;&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;可以让你更容易地实现一个自己的弱事件，但是性能非常差&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;使用&lt;a href=&quot;https://www.nuget.org/packages?q=weak+event+manager&amp;amp;prerel=false&quot;&gt;网上很多的 NuGet 包&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;下载量较高的几个 NuGet 包我都有研究过其中的源代码，要么有限制必须是定义事件的时候就必须使用弱事件，要么使用反射或其他动态调用方法性能较差&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;StackOverflow 上关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Weak Event&lt;/code&gt; 的高赞回答
    &lt;ul&gt;
      &lt;li&gt;目前还没有找到可以支持将任意事件添加弱事件支持的回答&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;由于我希望编写的弱事件机制尽可能减少对非预期框架的依赖，而且具有很高的性能，所以我打算自己实现一套。&lt;/p&gt;

&lt;h2 id=&quot;设计原则&quot;&gt;设计原则&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;支持为任意类型的事件添加弱事件支持，而不只是自己定义新事件的时候可以使用（对标主流 NuGet 包和 StackOverflow 上的回答）&lt;/li&gt;
  &lt;li&gt;具有很高的性能（对标主流的 NuGet 包和 WPF 泛型版本的 WeakEventManager）&lt;/li&gt;
  &lt;li&gt;类的使用者只需要编写极少量的代码就能完成（对标 WPF 非泛型版本的 WeakEventManager）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这三个原则，从上到下优先级依次降低。&lt;/p&gt;

&lt;p&gt;要支持所有类型的 CLR 事件，意味着我的设计中必须要能够直接监听到任意事件，而不能所有代码都从我自己编写的代码开始。&lt;/p&gt;

&lt;p&gt;要有很高的性能，就意味着我几乎不能使用“反射”，也不能使用委托的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DynamicInvoke&lt;/code&gt; 方法，还不能生成 IL 代码（首次生成很慢），也不能使用表达式树（首次编译很慢）。那么可以使用的也就只剩下两个了，一个是纯 C#/.NET 带的编译期就能确定执行的代码，另一个是使用 Roslyn 编译期在编译期间进行特殊处理。&lt;/p&gt;

&lt;p&gt;类的使用者要编写极少量的代码，意味着能够抽取到框架中的代码就尽量抽取到框架中。&lt;/p&gt;

&lt;h2 id=&quot;取名&quot;&gt;取名&lt;/h2&gt;

&lt;p&gt;俗话说，一个好的名字是成功的一半。&lt;/p&gt;

&lt;p&gt;因为我希望为任意 CLR 事件添加弱事件支持，所以其职责有点像“代理、中间人、中继、中转”，对应英文的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Proxy&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Agent&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Relay&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Transfer&lt;/code&gt;。最终我选择名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;Relay&lt;/code&gt;（中继），因为好听。&lt;/p&gt;

&lt;h2 id=&quot;api-设计&quot;&gt;API 设计&lt;/h2&gt;

&lt;p&gt;对于 API 的设计，我有一个小原则：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;如果技术实现很难，那么 API 迁就技术实现；如果技术实现很容易，那么技术迁就 API&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我总结了好的 API 设计的一些原则：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/framework-api-design&quot;&gt;好的框架需要好的 API 设计 —— API 设计的六个原则 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不得不说，此类型设计的技术难度还是挺大的。虽然我们知道有 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt; 可用，但依然存在很多的技术难点。于是 API 的设计可能要退而求其次优先满足前两个优先级更高的目标。&lt;/p&gt;

&lt;p&gt;我们期望 API 足够简单，因此在几个备选方案中选择：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventRelay.Subscribe(&quot;Changed&quot;, OnChanged)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;使用字符串来表示事件，肯定会用到反射，不可取&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventRelay.Subscribe(o =&amp;gt; o.Changed, OnChanged)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;如果使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt; 来做，会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;o.Changed&lt;/code&gt; 必须出现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 左边的编译错误&lt;/li&gt;
      &lt;li&gt;如果使用表达式树，也一样会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;o.Changed&lt;/code&gt; 必须出现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 左边的编译错误，同时还会出现少量性能问题&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此，直接一个方法就能完成事件注册是不可能的了，我们改用其他方法——继承自某个基类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*实现弱事件订阅*/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*实现弱事件注销*/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么实现的难点就都在 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 方法里面了。&lt;/p&gt;

&lt;h2 id=&quot;技术实现&quot;&gt;技术实现&lt;/h2&gt;

&lt;p&gt;我们究竟需要哪些信息才可以完成弱事件机制呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;事件源（也就是在使用弱事件机制之前最原始的事件引发者，经常以 &lt;code class=&quot;highlighter-rouge&quot;&gt;object sender&lt;/code&gt; 的形式出现在你的代码中）&lt;/li&gt;
  &lt;li&gt;要订阅的事件（比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher.Changed&lt;/code&gt; 事件）&lt;/li&gt;
  &lt;li&gt;新注册的事件处理函数（也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 方法中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而事情并没有那么简单：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;一&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在框架通用代码中，我不可能获取到要订阅的事件。因为事件要求只能出现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的左边，不能以任何其他形式使用（包括但不限于通过参数传递，伪装成 Lambda 表达式，伪装成表达式树）。这意味着 &lt;code class=&quot;highlighter-rouge&quot;&gt;o.Changed += OnChanged&lt;/code&gt; 这样的事件订阅完全写不出来通用代码（除非牺牲性能）。&lt;/p&gt;

&lt;p&gt;那么还能怎么做呢？只能将这段写不出来的代码留给业务编写者来编写了。&lt;/p&gt;

&lt;p&gt;也就是说，类似于 &lt;code class=&quot;highlighter-rouge&quot;&gt;o.Changed += OnChanged&lt;/code&gt; 这样的代码只能交给业务开发者来实现。与此同时也注定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnChanged&lt;/code&gt; 必须由业务开发者编写（因为无法写出通用的高性能的事件处理函数，并且还能在 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候保持同一个实例。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;二&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;我没有办法通过抽象的办法引发一个事件。具体来说，无法在抽象的通用代码中写出 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed.Invoke(sender, e)&lt;/code&gt; 这样代码。因为委托的基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;MultiCastDelegate&lt;/code&gt; 没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 方法可以使用，只有耗性能的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DynamicInvoke&lt;/code&gt; 方法。各种不同的委托定义虽然可以有相同的参数和返回值类型，但是却不能相互转换，因此我也不能将传入的委托转换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&amp;lt;TSender, TArgs&amp;gt;&lt;/code&gt; 这样的通用委托。&lt;/p&gt;

&lt;p&gt;庆幸的是，C# 提供了将方法组隐式转换委托的方法，可以让两个参数和返回值类型相同的委托隐式转换。但注意，这是隐式转换，没有运行时代码可以高性能地完成这件事情。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 方法中，&lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 参数就是使用方传入的事件处理函数，&lt;code class=&quot;highlighter-rouge&quot;&gt;value.Invoke&lt;/code&gt; 就是方法组，可以隐式转换为通用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&amp;lt;TSender, TArgs&amp;gt;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这意味着，我们可以将 &lt;code class=&quot;highlighter-rouge&quot;&gt;value.Invoke&lt;/code&gt; 传入来以通用的方式调用事件处理函数。但是请特别注意，这会导致新创建委托实例，导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候实例与 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的时候不一致，无法注销事件。因此，我们除了传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;value.Invoke&lt;/code&gt; 之外，还必须传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 本身。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API 半残品预览&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Unsubscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* 引发弱事件 */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这已经开始让业务方的代码变得复杂起来了。&lt;/p&gt;

&lt;h2 id=&quot;方案完善&quot;&gt;方案完善&lt;/h2&gt;

&lt;p&gt;我们还需要能够注册、注销和引发弱事件，而这部分就没那么坑了。因为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们已经把最坑的 &lt;code class=&quot;highlighter-rouge&quot;&gt;o.Changed += OnChanged&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;value.Invoke&lt;/code&gt; 都传进来了；&lt;/li&gt;
  &lt;li&gt;在类型中定义一个弱事件，目前网上各种主流弱事件 NuGet 包都有实现。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEvent&amp;lt;TSender, TArgs&amp;gt;&lt;/code&gt; 泛型类专门用来定义弱事件。&lt;/p&gt;

&lt;p&gt;不过，这让业务方的代码压力更大了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后，订阅事件所需的实例，我认为最好不要能够让业务方直接能访问。因为弱事件的实现并不简单（看上面如此复杂的公开 API 就知道了），如果能够直接访问，势必带来更复杂的使用问题。所以我仅在部分方法和 Lambda 表达式参数中开放实例。&lt;/p&gt;

&lt;p&gt;所以，构造函数需要传入事件源。&lt;/p&gt;

&lt;h2 id=&quot;最后的问题&quot;&gt;最后的问题&lt;/h2&gt;

&lt;p&gt;最后还留下了一个问题&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;订阅者现在确实“弱事件”了，但这个“中继”怎么办？可是被强引用了啊？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;虽然中继的类实例小得多，但这确实依然也是泄漏，因此需要回收。&lt;/p&gt;

&lt;p&gt;于是我在任何可能执行代码的时机加上了回收检查：如果发现所有订阅者都已经被回收，那么“中继”也就可以被回收了，将注销所有事件源的订阅。（当然要允许重新开始订阅。）&lt;/p&gt;

&lt;p&gt;所以最后业务方编写的中继代码又多了一些：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WeakEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnReferenceLost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;实际使用&quot;&gt;实际使用&lt;/h2&gt;

&lt;p&gt;虽然弱事件中继的代码复杂了点，但是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; 最终用户的使用可是非常简单的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\Desktop\walterlv.demo.md&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenamedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt; 是在懒得写，我可以加上 Roslyn 编译器生成中继代码的方式，这个我将在不久的将来加入到 Walterlv.WeakEvents 库中。&lt;/p&gt;

&lt;h2 id=&quot;相关源码&quot;&gt;相关源码&lt;/h2&gt;

&lt;p&gt;更具体的使用场景和示例代码，请阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event-relay&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文所涉及的全部源代码，已在 GitHub 上开源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Walterlv.Packages/tree/master/src/Utils/Walterlv.WeakEvents&quot;&gt;Walterlv.Packages/src/Utils/Walterlv.WeakEvents at master · walterlv/Walterlv.Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;注意开源协议：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://996.icu&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/link-996.icu-red.svg&quot; alt=&quot;996.icu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/996icu/996.ICU/blob/master/LICENSE&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/license-NPL%20(The%20996%20Prohibited%20License)-blue.svg&quot; alt=&quot;LICENSE&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/weak-event-patterns&quot;&gt;Weak Event Patterns - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 18 Sep 2019 13:59:58 +0000</pubDate>
        <link>https://blog.walterlv.com/post/design-a-dotnet-weak-event-relay.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/design-a-dotnet-weak-event-relay.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）</title>
        <description>&lt;p&gt;弱引用是 .NET 引入的概念，可以用来协助解决内存泄漏问题。然而事件也可能带来内存泄漏问题，是否有弱事件机制可以使用呢？.NET 没有自带的弱事件机制，但其中的一个子集 WPF 带了。然而我们不是什么项目都能引用 WPF 框架类库的。网上有很多弱事件的 NuGet 包，不过仅仅支持定义事件的时候写成弱事件而不支持让任意事件变成弱事件，并且存在性能问题。&lt;/p&gt;

&lt;p&gt;本文介绍 Walterlv.WeakEvents 库来做弱事件。你可以借此将任何一个 CLR 事件当作弱事件来使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;系列博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/implement-custom-dotnet-weak-event-relay&quot;&gt;.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件（可让任意 CLR 事件成为弱事件）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-dotnet-weak-event-relay&quot;&gt;.NET 设计一套高性能的弱事件机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;场景与问题&quot;&gt;场景与问题&lt;/h2&gt;

&lt;p&gt;了解一下场景，你就能知道这是否是适合你的方案。&lt;/p&gt;

&lt;p&gt;比如我正在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 来监听一个文件的改变，我可能会使用到这些事件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Created&lt;/code&gt; 在文件被创建时引发&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 在文件内容或属性发生改变时引发&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Renamed&lt;/code&gt; 在文件被重命名时引发&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Deleted&lt;/code&gt; 在文件被删除时引发&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更具体一点的代码是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;D:\Desktop\walterlv.demo.md&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenamedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 使用 demo&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 此方法结束后，demo 将脱离作用域，本应该可以被回收的。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，一旦我们这么写，那么我们这个类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemo&lt;/code&gt; 的实例 &lt;code class=&quot;highlighter-rouge&quot;&gt;demo&lt;/code&gt; 将无法被回收，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 将始终通过事件引用着这个实例。即使你已经不再引用这个类型的任何一个实例，此实例也会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;_watcher&lt;/code&gt; 的事件引用着，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 的实例也因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnableRaisingEvents&lt;/code&gt; 而一直存在。&lt;/p&gt;

&lt;p&gt;一个可行的解决办法是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 方法。不过有些时候很难决定到底在什么时机调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 合适。&lt;/p&gt;

&lt;p&gt;现在，我们希望有一种方法，能够在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemo&lt;/code&gt; 的实例失去作用域后被回收，最好 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 也能够自动被 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 释放掉。&lt;/p&gt;

&lt;p&gt;如果你试图解决的是类似这样的问题，那么本文就可以帮到你。&lt;/p&gt;

&lt;p&gt;总结一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;用到了一个现有的类型（你无法修改它的源代码，本例中是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt;）；&lt;/li&gt;
  &lt;li&gt;你无法决定什么时候释放此类型的实例（本例中是不知道什么时候调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt;）；&lt;/li&gt;
  &lt;li&gt;一旦你监听此类型的事件，将产生内存泄漏，导致你自己类型的实例无法释放（本例中是 &lt;code class=&quot;highlighter-rouge&quot;&gt;demo&lt;/code&gt; 变量脱离作用域。）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;目前有 WPF 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventManager&lt;/code&gt; 机制，网上也有&lt;a href=&quot;https://www.nuget.org/packages?q=weak+event+manager&amp;amp;prerel=false&quot;&gt;很多可用的 NuGet 包&lt;/a&gt;，但是都有限制：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;只能给自己定义的类型引入弱事件机制，不能给现有类型引入弱事件；&lt;/li&gt;
  &lt;li&gt;要么用反射，要么用 IL 生成代码，性能都不高。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而 Walterlv.WeakEvents 除了解决了给任一类型引入弱事件的问题，还具有非常高的性能，几乎跟定义原生事件无异。&lt;/p&gt;

&lt;h2 id=&quot;下载安装-walterlvweakevents&quot;&gt;下载安装 Walterlv.WeakEvents&lt;/h2&gt;

&lt;p&gt;在你需要做弱事件的项目中安装 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.WeakEvents/&quot;&gt;Walterlv.WeakEvents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;编写自定义的弱事件中继&quot;&gt;编写自定义的弱事件中继&lt;/h2&gt;

&lt;p&gt;现在，我们需要编写一个自定义的弱事件中继类 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/code&gt;，即专门为 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 做的弱事件中继。&lt;/p&gt;

&lt;p&gt;下面是一个简单点的例子，为其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 事件做了一个中继：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WeakEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnReferenceLost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能会看到代码有点儿多，但是我向你保证，这是除了采用 Roslyn 编译器技术以外最高性能的方案了。如果你对弱事件的性能有要求，那么还是接受这些代码会比较好。&lt;/p&gt;

&lt;p&gt;不要紧张，我来一一解释这些代码。另外，如果你不想懂这些代码，就按照模板一个个敲就好了，都是模板化的代码（特别适合使用 Roslyn 编译器生成，我可能接下来就会做这件事情避免你写出这些代码）。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;首先，我们定义了一个自定义的弱事件中继 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/code&gt;，继承自库 Walterlv.WeakEvents 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEventRelay&amp;lt;FileSystemWatcher&amp;gt;&lt;/code&gt; 类型。带上的泛型参数表明是针对 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 类型做弱事件中继。&lt;/li&gt;
  &lt;li&gt;一个构造函数，将参数传递给基类：&lt;code class=&quot;highlighter-rouge&quot;&gt;public FileSystemWatcherWeakEventRelay(FileSystemWatcher eventSource) : base(eventSource) { }&lt;/code&gt;。这个构造函数是可以用 Visual Studio 生成的，快捷键是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + Enter&lt;/code&gt;（快捷键功效详见：&lt;a href=&quot;/post/keyboard-shortcuts-to-improve-the-efficiency-of-visual-studio&quot;&gt;提高使用 Visual Studio 开发效率的键盘快捷键&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;定义了一个私有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakEvent&amp;lt;FileSystemEventArgs&amp;gt;&lt;/code&gt;，名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_changed&lt;/code&gt;，这个就是弱事件的核心。泛型参数是事件参数的类型（注意，为了极致的性能，这里的泛型参数是事件参数的名称，而不是大多数弱事件框架中提供的事件处理委托类型）。&lt;/li&gt;
  &lt;li&gt;定义了一个对外公开的事件 &lt;code class=&quot;highlighter-rouge&quot;&gt;public event FileSystemEventHandler Changed&lt;/code&gt;。
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 方法固定调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Subscribe(o =&amp;gt; o.Changed += OnChanged, () =&amp;gt; _changed.Add(value, value.Invoke));&lt;/code&gt;。其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 中的事件，&lt;code class=&quot;highlighter-rouge&quot;&gt;OnChanged&lt;/code&gt; 是我们即将定义的事件处理函数，&lt;code class=&quot;highlighter-rouge&quot;&gt;_changed&lt;/code&gt; 是前面定义好的弱事件字段，而后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;value.Invoke&lt;/code&gt; 是固定写法。&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 方法固定调用弱事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 方法，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;_changed.Remove(value);&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;编写针对公开事件的事件处理函数 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnChanged&lt;/code&gt;，并在里面固定调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryInvoke(_changed, sender, e)&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnReferenceLost&lt;/code&gt; 方法，用于在对象已被回收后反注册 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 中的事件。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;希望看了上面这 6 点之后你还能理解这些代码都是在做啥。如果依然不能理解，可以考虑：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;参考下面 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/code&gt; 的完整代码来理解哪些是可变部分哪些是不可变部分，自己替换就好；&lt;/li&gt;
  &lt;li&gt;等待 Walterlv.WeakEvents 库的作者更新自动生成这段代码的功能。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WeakEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcherWeakEventRelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenamedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenamedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenamedEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_renamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_renamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenamedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_renamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_deleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnReferenceLost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRenamed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用自定义的弱事件中继&quot;&gt;使用自定义的弱事件中继&lt;/h2&gt;

&lt;p&gt;当你把上面这个自定义的弱事件中继类型写好了之后，使用它就非常简单了，对我们原有的代码改动非常小。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class WalterlvDemo
    {
        public WalterlvDemo()
        {
            _watcher = new FileSystemWatcher(@&quot;D:\Desktop\walterlv.demo.md&quot;)
            {
                EnableRaisingEvents = true,
            };
&lt;span class=&quot;gi&quot;&gt;++          var weakEvent = new FileSystemWatcherWeakEventRelay(_watcher);
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--          _watcher.Created += OnCreated;
--          _watcher.Changed += OnChanged;
--          _watcher.Renamed += OnRenamed;
--          _watcher.Deleted += OnDeleted;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          weakEvent.Created += OnCreated;
++          weakEvent.Changed += OnChanged;
++          weakEvent.Renamed += OnRenamed;
++          weakEvent.Deleted += OnDeleted;
&lt;/span&gt;        }

        private readonly FileSystemWatcher _watcher;
        private void OnCreated(object sender, FileSystemEventArgs e) { }
        private void OnChanged(object sender, FileSystemEventArgs e) { }
        private void OnRenamed(object sender, RenamedEventArgs e) { }
        private void OnDeleted(object sender, FileSystemEventArgs e) { }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;最终效果预览&quot;&gt;最终效果预览&lt;/h2&gt;

&lt;p&gt;我写了一个程序，每 1 秒修改一次文件；每 5 秒回收一次内存。然后使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 来监视这个文件的改变。&lt;/p&gt;

&lt;p&gt;可以看到，在回收内存之后，将不会再监视文件的改变。当然，如果你期望一直可以监视改变，当然也不希望用到本文的弱事件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-18-20-41-40.png&quot; alt=&quot;可以回收事件&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;为什么弱事件中继的-api-如此设计&quot;&gt;为什么弱事件中继的 API 如此设计？&lt;/h2&gt;

&lt;p&gt;一句话解答：&lt;strong&gt;为了高性能&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;请参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-dotnet-weak-event-relay&quot;&gt;.NET 设计一套高性能的弱事件机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/weak-event-patterns&quot;&gt;Weak Event Patterns - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 18 Sep 2019 13:59:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/implement-custom-dotnet-weak-event-relay.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/implement-custom-dotnet-weak-event-relay.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 中启动进程时所使用的 UseShellExecute 设置为 true 和 false 分别代表什么意思？</title>
        <description>&lt;p&gt;在 .NET 中创建进程时，可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessStartInfo&lt;/code&gt; 类的一个新实例。在此类型中，有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute&lt;/code&gt; 属性。&lt;/p&gt;

&lt;p&gt;本文介绍 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute&lt;/code&gt; 属性的作用，设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 时，分别有哪些进程启动行为上的差异。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;本质差异&quot;&gt;本质差异&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 本质上是启动一个新的子进程，不过这个属性的不同，使得启动进程的时候会调用不同的 Windows 的函数。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute = true&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;调用的是 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutea&quot;&gt;ShellExecute&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute = false&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;调用的是 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa&quot;&gt;CreateProcess&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，如果你知道这两个函数的区别，那你自然也就了解此属性设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 的区别了。&lt;/p&gt;

&lt;h2 id=&quot;效果差异&quot;&gt;效果差异&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ShellExecute&lt;/code&gt; 的用途是打开程序或者文件或者其他任何能够打开的东西（如网址）。&lt;/p&gt;

&lt;p&gt;也就是说，你可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 的时候传入这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一个可执行程序（exe）&lt;/li&gt;
  &lt;li&gt;一个网址&lt;/li&gt;
  &lt;li&gt;一个 html / mp4 / jpg / docx / enbx 等各种文件&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;PATH&lt;/code&gt; 环境变量中的各种程序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不过，此方法有一些值得注意的地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不支持重定向输入和输出&lt;/li&gt;
  &lt;li&gt;最终启动了哪个进程可能是不确定的，你可能需要注意潜在的安全风险&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateProcess&lt;/code&gt; 则会精确查找路径来执行，不支持各种非可执行程序的打开。但是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;支持重定向输入和输出&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如何选择&quot;&gt;如何选择&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute&lt;/code&gt; 在 .NET Framework 中的的默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，在 .NET Core 中的默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果有以下需求，那么建议设置此值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;需要明确执行一个已知的程序&lt;/li&gt;
  &lt;li&gt;需要重定向输入和输出&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你有以下需求，那么建议设置此值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 或者保持默认：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;需要打开文档、媒体、网页文件等&lt;/li&gt;
  &lt;li&gt;需要打开 Url&lt;/li&gt;
  &lt;li&gt;需要打开脚本执行&lt;/li&gt;
  &lt;li&gt;需要打开计算机上环境变量中路径中的程序&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/5255335/6233938&quot;&gt;c# - When do we need to set UseShellExecute to True? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 17 Sep 2019 04:37:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-shell-execute-in-process-start-info.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-shell-execute-in-process-start-info.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET Framework 的 bug？try-catch-when 中如果 when 语句抛出异常，程序将彻底崩溃</title>
        <description>&lt;p&gt;在 .NET Framework 4.8 中，try-catch-when 中如果 when 语句抛出异常，程序将彻底崩溃。而 .NET Core 3.0 中不会出现这样的问题。&lt;/p&gt;

&lt;p&gt;本文涉及的 Bug 已经报告给了微软，并且得到了微软的回复。是 .NET Framework 4.8 为了解决一个安全性问题而强行结束了进程。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/try-catch-when-causes-app-crash.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/try-catch-when-causes-app-crash-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;官方文档中-when-的行为&quot;&gt;官方文档中 when 的行为&lt;/h2&gt;

&lt;p&gt;你可以前往官方文档：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/standard/exceptions/using-user-filtered-exception-handlers&quot;&gt;使用用户筛选的异常处理程序 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在其中，你可以找到这样一段话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;用户筛选的子句的表达式不受任何限制。 如果在执行用户筛选的表达式期间发生异常，则将放弃该异常，并视筛选表达式的值为 false。 在这种情况下，公共语言运行时继续搜索当前异常的处理程序。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即当 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 块中出现异常时，&lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 表达式将视为值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，并且此异常将被忽略。&lt;/p&gt;

&lt;h2 id=&quot;示例程序&quot;&gt;示例程序&lt;/h2&gt;

&lt;p&gt;鉴于官方文档中的描述，我们可以编写一些示例程序来验证这样的行为。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.CatchWhenCrash&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Try&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;End&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很显然，我们直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileNotFoundException&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileName&lt;/code&gt; 属性会保持为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。对其解引用会产生 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;。很显然代码不应该这么写，但可以用来验证 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 语句的行为。&lt;/p&gt;

&lt;p&gt;按照官网描述，输出应该为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;Catch 2&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt;。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 中的异常被忽略，因此不会进入到外层的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块中；因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 中出现异常导致表达式值视为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，因此进入了更合适的异常处理块 &lt;code class=&quot;highlighter-rouge&quot;&gt;Catch 2&lt;/code&gt; 中。&lt;/p&gt;

&lt;h2 id=&quot;在-net-core-30-中的行为和-net-framework-48-中的行为&quot;&gt;在 .NET Core 3.0 中的行为和 .NET Framework 4.8 中的行为&lt;/h2&gt;

&lt;p&gt;下面两张图分别是这段代码在 .NET Core 3.0 和 .NET Framework 4.8 中的输出：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-06-35.png&quot; alt=&quot;.NET Core 3.0 中的行为&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-08-21.png&quot; alt=&quot;.NET Framework 4.8 中的行为&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以注意到，只有 .NET Core 3.0 中的行为符合官方文档的描述，而 .NET Framework 4.8 中甚至连 &lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt; 都没有输出！几乎可以确定，程序在 .NET Framework 4.8 中出现了致命的崩溃！&lt;/p&gt;

&lt;p&gt;如果我们以 Visual Studio 调试启动此程序，可以看到抛出了 CLR 异常：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-10-46.png&quot; alt=&quot;抛出了 CLR 异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以下是在 Visual Studio 中单步跟踪的步骤：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-catch-when-crash.gif&quot; alt=&quot;单步调试&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;issue-和行为&quot;&gt;Issue 和行为&lt;/h2&gt;

&lt;p&gt;由于本人金鱼般的记忆力，我竟然给微软报了三次这个 Bug：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;给文档的（2019.09.10）：&lt;a href=&quot;https://github.com/dotnet/docs/issues/14338&quot;&gt;When use the when keyword in a catch expression the app crashes instead of do what the document says · Issue #14338 · dotnet/docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;给框架和 SDK 的（2019.09.12）： &lt;a href=&quot;https://github.com/dotnet/corefx/issues/41047&quot;&gt;When use the when keyword in a catch expression the app crashes instead of do what the document says · Issue #41047 · dotnet/corefx&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;给运行时的（2019.07.02）：&lt;a href=&quot;https://github.com/dotnet/coreclr/issues/25534&quot;&gt;App will crash when using the when keyword in a catch expression · Issue #25534 · dotnet/coreclr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此问题是 .NET Framework 4.8 为了修复一个安全性问题才强行结束了进程：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Process corrupting exceptions in exception filter (like access violation) now result in aborting the current process. [110375, clr.dll, Bug, Build:3694]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/dotnet/blob/master/releases/net48/dotnet48-changes.md&quot;&gt;dotnet/dotnet48-changes.md at master · microsoft/dotnet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 12 Sep 2019 06:58:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/try-catch-when-causes-app-crash.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/try-catch-when-causes-app-crash.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何在 WPF 中获取所有已经显式赋过值的依赖项属性</title>
        <description>&lt;p&gt;获取 WPF 的依赖项属性的值时，会依照优先级去各个级别获取。这样，无论你什么时候去获取依赖项属性，都至少是有一个有效值的。有什么方法可以获取哪些属性被显式赋值过呢？如果是 CLR 属性，我们可以自己写判断条件，然而依赖项属性没有自己写判断条件的地方。&lt;/p&gt;

&lt;p&gt;本文介绍如何获取以及显式赋值过的依赖项属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;需要用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject.GetLocalValueEnumerator()&lt;/code&gt; 方法来获得一个可以遍历所有依赖项属性本地值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoWhatYouLikeByWalterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enumerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLocalValueEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MoveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在这里使用 property 和 value。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;BindingExpression&lt;/code&gt; 还可能是其他一些可能延迟计算值的提供者。因此，你不能在这里获取到常规方法获取到的依赖项属性的真实类型的值。&lt;/p&gt;

&lt;p&gt;但是，此枚举拿到的所有依赖项属性的值都是此依赖对象已经赋值过的依赖项属性的本地值。如果没有赋值过，将不会在这里的遍历中出现。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-properties-overview&quot;&gt;Dependency properties overview - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 12 Sep 2019 03:39:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-get-local-value-enumerator.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-get-local-value-enumerator.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 WPF 中获取一个依赖对象的所有依赖项属性</title>
        <description>&lt;p&gt;本文介绍如何在 WPF 中获取一个依赖对象的所有依赖项属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;通过-wpf-标记获取&quot;&gt;通过 WPF 标记获取&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumerateDependencyProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;MarkupObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMarkupObjectFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MarkupProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumerateAttachedProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;MarkupObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMarkupObjectFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MarkupProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markupObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;通过设计器专用方法获取&quot;&gt;通过设计器专用方法获取&lt;/h2&gt;

&lt;p&gt;本来 .NET 中提供了一些专供设计器使用的类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeDescriptor&lt;/code&gt; 可以帮助设计器找到一个类型或者组件的所有可以设置的属性，不过我们也可以通过此方法来获取所有可供使用的属性。&lt;/p&gt;

&lt;p&gt;下面是带有重载的两个方法，一个传入类型一个传入实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 获取一个对象中所有的依赖项属性。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDependencyProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyFilterAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyFilterOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 获取一个类型中所有的依赖项属性。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDependencyProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyFilterAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyFilterOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/26367132/6233938&quot;&gt;wpf - How to enumerate all dependency properties of control? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/580234cb-e870-4af1-9a91-3e3ba118c89c/getting-list-of-all-dependencyattached-properties-of-an-object?forum=wpf&quot;&gt;Getting list of all dependency/attached properties of an Object&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 12 Sep 2019 03:24:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-get-all-dependency-properties.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-get-all-dependency-properties.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>临时编写和调试 C++ 代码？用 VSCode 就够了！一分钟搭好 C++ 调试环境</title>
        <description>&lt;p&gt;突然间要编写或者调试几个 C++ 的小程序，动用 Visual Studio 创建一个解决方案显得大了些。如果能够利用随时随地就方便打开的 Visual Studio Code 来开发，则清爽很多。&lt;/p&gt;

&lt;p&gt;本文教你一分钟在 Visual Studio Code 中搭建好 C++ 开发环境。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;本文大纲&quot;&gt;本文大纲&lt;/h2&gt;

&lt;p&gt;本文总共分为三个步骤，每个步骤都非常简单。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步安装扩展&quot;&gt;第一步：安装扩展&lt;/h2&gt;

&lt;p&gt;你需要在 Visual Studio Code 中安装 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools&quot;&gt;C/C++&lt;/a&gt; 扩展。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-30-54.png&quot; alt=&quot;安装 C++ 扩展&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步启动-vscode&quot;&gt;第二步：启动 VSCode&lt;/h2&gt;

&lt;p&gt;注意，安装完成后，要通过 Visual Studio 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Developer Command Prompt for VS 2019&lt;/code&gt; 来启动 Visual Studio Code。这样才可以获得 Visual Studio 2019 自带的各种编译工具路径的环境变量。Visual Studio Code 就可以无缝使用 Visual Studio 2019 附带的那些工具。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-32-39.png&quot; alt=&quot;启动 Developer Command Prompt for VS 2019&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，在新启动的命令行工具中启动 Visual Studio Code。&lt;/p&gt;

&lt;p&gt;输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;code&lt;/code&gt; 即可启动：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果已有线程的路径，可以带上路径的命令行参数：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\Walterlv.CppDemo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-36-42.png&quot; alt=&quot;启动 Visual Studio Code&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步f5-运行&quot;&gt;第三步：F5 运行&lt;/h2&gt;

&lt;p&gt;随便在目录中新建一个文件，写上 C++ 代码。比如在 &lt;code class=&quot;highlighter-rouge&quot;&gt;example.cpp&lt;/code&gt; 文件中写上如下代码：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&amp;lt;iostream&amp;gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;welcome to blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按下 F5，选择对应的 C++ 编译平台（我这里选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;C++ (Windows)&lt;/code&gt;），然后选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;cl.exe build and debug active file&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-41-01.png&quot; alt=&quot;选择编译平台&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cl.exe build and debug active file&lt;/code&gt; 的目的是调试当前激活的文件，这样的调试方式在 python/java 等语言中大家屡见不鲜，好处是对于小型代码调试起来非常简单直接。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-42-26.png&quot; alt=&quot;选择调试当前文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来 Visual Studio Code 就会生成一些调试所需的配置文件。&lt;/p&gt;

&lt;p&gt;再次按下 F5，Visual Studio Code 会提示没有编译任务，点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configure Task&lt;/code&gt;，随后选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;C/C++: cl.exe build active file&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-47-09.png&quot; alt=&quot;Configure Task&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-47-26.png&quot; alt=&quot;C/C++: cl.exe build active file&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来 Visual Studio Code 就会生成一些编译所需的配置文件。&lt;/p&gt;

&lt;p&gt;再次按下 F5 就可以直接编译 &lt;code class=&quot;highlighter-rouge&quot;&gt;example.cpp&lt;/code&gt; 文件然后运行调试了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-48-53.png&quot; alt=&quot;调试当前文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;输出在 Debug Console 里面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-09-49-57.png&quot; alt=&quot;Debug Console&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其他注意事项&quot;&gt;其他注意事项&lt;/h2&gt;

&lt;p&gt;如果你给 Visual Studio 设置了非默认的终端，那么需要注意：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;应该使用 PowerShell 系列的终端（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh&lt;/code&gt;）不能使用 bash 系列的终端。因为 Windows 下工具使用的路径格式是反斜杠 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;，而 bash 系列终端使用的路径是斜杠 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;。如果使用 bash 终端，编译工具会因为路径问题导致编译失败。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，不要怪我说我是这么编写教程的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;首先，我们已知 1+1=2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-10-06-06.png&quot; alt=&quot;1+1=2&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;于是可以推导出……&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-07-10-06-10.png&quot; alt=&quot;推导出&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 07 Sep 2019 02:07:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/temperarly-debug-cpp-in-vscode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/temperarly-debug-cpp-in-vscode.html</guid>
        
        
        <category>cpp</category>
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>在使用 .NET Remoting 技术开发跨进程通信时可能遇到的各种异常</title>
        <description>&lt;p&gt;在使用 .NET Remoting 开发跨进程应用的时候，你可能会遇到一些异常。因为这些异常在后验的时候非常简单但在一开始有各种异常烦扰的时候却并不清晰，所以我将这些异常整理到此文中，方便小伙伴们通过搜索引擎查阅。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;连接到-ipc-端口失败-系统找不到指定的文件&quot;&gt;连接到 IPC 端口失败: 系统找不到指定的文件&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.Remoting.RemotingException:“连接到 IPC 端口失败: 系统找不到指定的文件。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;或者英文版：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.Remoting.RemotingException: Failed to connect to an IPC Port: The system cannot find the file specified.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;出现此异常时，说明你获取到了一个远端对象，但是在使用此对象的时候，甚至还没有注册 IPC 端口。&lt;/p&gt;

&lt;p&gt;比如，下面的代码是注册一个 IPC 端口的一种比较粗暴的写法，传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;portName&lt;/code&gt; 是 IPC 的 Uri 路径前缀。例如我可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，这样一个 IPC 对象的格式大约类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;ipc://walterlv/xxx&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;portName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BinaryServerFormatterSinkProvider&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TypeFilterLevel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeFilterLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Full&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BinaryClientFormatterSinkProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hashtable&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;portName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;portName&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IpcChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ChannelServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当试图访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;ipc://walterlv/foo&lt;/code&gt; 对象并调用其中的方法的时候，如果连 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 端口都没有注册，就会出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;连接到 IPC 端口失败: 系统找不到指定的文件。&lt;/code&gt; 异常。&lt;/p&gt;

&lt;p&gt;如果你已经注册了 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 端口，但是没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; 对象，则会出现另一个错误 &lt;code class=&quot;highlighter-rouge&quot;&gt;找不到请求的服务&lt;/code&gt;，请看下一节。&lt;/p&gt;

&lt;h2 id=&quot;找不到请求的服务&quot;&gt;找不到请求的服务&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.Remoting.RemotingException:“找不到请求的服务”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;或者英文版：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.Remoting.RemotingException: Requested Service not found&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当出现此异常时，可能的原因有三个：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;要查找的远端对象尚未创建；&lt;/li&gt;
  &lt;li&gt;要查找的远端对象已被回收；&lt;/li&gt;
  &lt;li&gt;没有使用匹配的方法创建和访问对象。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;更具体来说，对于第一种情况，就是当你试图跨进程访问某对象的时候，此对象还没有创建。你需要做的，是控制好对象创建的时机，创建对象的进程需要比访问它的进程更早完成对象的创建和封送。也就是下面的代码需要先调用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;RemotingServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而对于第二种情况，你可能需要手动处理好封送对象的生命周期。重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;InitializeLifetimeService&lt;/code&gt; 方法并返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 是一个很偷懒却有效的方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Remoting.Framework&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoteObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarshalByRefObject&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitializeLifetimeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而对于第三种情况，你需要检查你是如何注册 .NET Remoting 通道的，创建和访问方式必须匹配。&lt;/p&gt;

&lt;h2 id=&quot;信道ipc已注册&quot;&gt;信道“ipc”已注册&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.Remoting.RemotingException:“信道“ipc”已注册。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在同一个进程中，&lt;code class=&quot;highlighter-rouge&quot;&gt;IpcChannel&lt;/code&gt; 类的默认信道名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;IpcChannel.ChannelName&lt;/code&gt; 值是字符串 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;ipc&quot;&lt;/code&gt;。如果你不通过它的参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;properties&lt;/code&gt; 来指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;[&quot;name&quot;] = &quot;另一个名称&quot;&lt;/code&gt;，那么你就不能重复调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ChannelServices.RegisterChannel&lt;/code&gt; 来调用这个信道。&lt;/p&gt;

&lt;p&gt;说简单点，就是上面的方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;RegisterChannel&lt;/code&gt; 你不能在一个进程中调用两次，即便 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;portName&quot;&lt;/code&gt; 不同也不行。通常你也不需要去调用两次，如果一定要，请通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;HashTable&lt;/code&gt; 修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; 属性。&lt;/p&gt;

&lt;!-- ## 创建 IPC 端口失败: 拒绝访问

&gt; System.Runtime.Remoting.RemotingException:“创建 IPC 端口失败: 拒绝访问。”
 --&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44373484/net-remoting-error-requested-service-not-found&quot;&gt;c# - .Net remoting error “Requested Service not found” - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 05 Sep 2019 13:26:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-remoting-exceptions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-remoting-exceptions.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET/C# 阻止屏幕关闭，阻止系统进入睡眠状态</title>
        <description>&lt;p&gt;在 Windows 系统中，一段时间不操作键盘和鼠标，屏幕便会关闭，系统会进入睡眠状态。但有些程序（比如游戏、视频和演示文稿）在运行过程中应该阻止屏幕关闭，否则屏幕总是关闭，会导致体验会非常糟糕。&lt;/p&gt;

&lt;p&gt;本文介绍如何编写 .NET/C# 代码临时阻止屏幕关闭以及系统进入睡眠状态。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-api&quot;&gt;Windows API&lt;/h2&gt;

&lt;p&gt;我们需要使用到一个 Windows API：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Enables an application to inform the system that it is in use, thereby preventing the system from entering sleep or turning off the display while the application is running.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetThreadExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;esFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用到的枚举用 C# 类型定义是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Forces the system to be in the working state by resetting the system idle timer.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SystemRequired&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Forces the display to be on by resetting the display idle timer.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DisplayRequired&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// This value is not supported. If &amp;lt;see cref=&quot;UserPresent&quot;/&amp;gt; is combined with other esFlags values, the call will fail and none of the specified states will be set.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Obsolete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This value is not supported.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UserPresent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Enables away mode. This value must be specified with &amp;lt;see cref=&quot;Continuous&quot;/&amp;gt;.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;para /&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Away mode should be used only by media-recording and media-distribution applications that must perform critical background processing on desktop computers while the computer appears to be sleeping.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AwaymodeRequired&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Informs the system that the state being set should remain in effect until the next call that uses &amp;lt;see cref=&quot;Continuous&quot;/&amp;gt; and one of the other state flags is cleared.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Continuous&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x80000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上所有的注释均照抄自微软的官方 API 文档：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate&quot;&gt;SetThreadExecutionState function (winbase.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;api-封装&quot;&gt;API 封装&lt;/h2&gt;

&lt;p&gt;如果你擅长阅读英文，那么以上的 API 函数、枚举和注释足够你完成你的任务了。&lt;/p&gt;

&lt;p&gt;不过，我这里提供一些封装，以应对一些常用的场景。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含控制屏幕关闭以及系统休眠相关的方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SystemSleep&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 设置此线程此时开始一直将处于运行状态，此时计算机不应该进入睡眠状态。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 此线程退出后，设置将失效。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 如果需要恢复，请调用 &amp;lt;see cref=&quot;RestoreForCurrentThread&quot;/&amp;gt; 方法。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;keepDisplayOn&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 表示是否应该同时保持屏幕不关闭。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 对于游戏、视频和演示相关的任务需要保持屏幕不关闭；而对于后台服务、下载和监控等任务则不需要。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PreventForCurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keepDisplayOn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetThreadExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keepDisplayOn&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continuous&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemRequired&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayRequired&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continuous&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemRequired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 恢复此线程的运行状态，操作系统现在可以正常进入睡眠状态和关闭屏幕。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RestoreForCurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetThreadExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continuous&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 重置系统睡眠或者关闭屏幕的计时器，这样系统睡眠或者屏幕能够继续持续工作设定的超时时间。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;keepDisplayOn&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 表示是否应该同时保持屏幕不关闭。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 对于游戏、视频和演示相关的任务需要保持屏幕不关闭；而对于后台服务、下载和监控等任务则不需要。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResetIdle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keepDisplayOn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetThreadExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keepDisplayOn&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemRequired&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayRequired&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutionState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemRequired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你对这段封装中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;keepDisplayOn&lt;/code&gt; 参数，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExecutionState.DisplayRequired&lt;/code&gt; 枚举不了解，看看下图直接就懂了。一个指的是屏幕关闭，一个指的是系统进入睡眠。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-05-14-48-37.png&quot; alt=&quot;电源和睡眠&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此封装后，使用则相当简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 阻止系统睡眠，阻止屏幕关闭。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SystemSleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PreventForCurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 恢复此线程曾经阻止的系统休眠和屏幕关闭。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SystemSleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RestoreForCurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 重置系统计时器，临时性阻止系统睡眠和屏幕关闭。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 此效果类似于手动使用鼠标或键盘控制了一下电脑。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SystemSleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResetIdle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PreventForCurrentThread&lt;/code&gt; 这个 API 的时候，你需要避免程序对空闲时机的控制不好，导致屏幕始终不关闭。&lt;/p&gt;

&lt;p&gt;如果你发现无论你设置了多么短的睡眠时间和屏幕关闭时间，屏幕都不会关闭，那就是有某个程序阻止了屏幕关闭，你可以：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/find-out-the-reason-that-wakes-the-pc-up&quot;&gt;查看有哪些程序会一直保持屏幕处于打开状态&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/find-out-the-reason-that-wakes-the-pc-up&quot;&gt;找到是谁持续唤醒了计算机屏幕&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate&quot;&gt;SetThreadExecutionState function (winbase.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 05 Sep 2019 06:49:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/set-thread-execution-state.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/set-thread-execution-state.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>git fetch 失败，因为 unable to resolve reference 'refs/remotes/origin/xxx': reference broken</title>
        <description>&lt;p&gt;我在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git fetch&lt;/code&gt; 命令的时候，发现竟然会失败，提示错误 &lt;code class=&quot;highlighter-rouge&quot;&gt;error: cannot lock ref 'refs/remotes/origin/xxx': unable to resolve reference 'refs/remotes/origin/xxx': reference broken&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;本文介绍如何修复这样的错误，并探索此错误产生的原因。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;错误&quot;&gt;错误&lt;/h2&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git fetch&lt;/code&gt; 命令之后，发现竟然出现了错误，错误输出如下：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git fetch &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prune&lt;/span&gt;
Fetching origin
error: cannot lock ref &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/next/release'&lt;/span&gt;: unable to resolve reference &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/next/release'&lt;/span&gt;: reference broken
From git&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;.&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;.com:walterlv/demo-project
 &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;new branch]            next/release        -&amp;gt; origin/next/release  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;unable to update &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;ref&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
error: cannot lock ref &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/feature/ai'&lt;/span&gt;: unable to resolve reference &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/feature/ai'&lt;/span&gt;: reference broken
 &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;new branch]            feature/ai          -&amp;gt; origin/feature/ai  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;unable to update &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;ref&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
error: cannot lock ref &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/release'&lt;/span&gt;: unable to resolve reference &lt;span class=&quot;s1&quot;&gt;'refs/remotes/origin/release'&lt;/span&gt;: reference broken
 &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;new branch]            release             -&amp;gt; origin/release  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;unable to update &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;ref&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
error: Could not fetch origin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;修复&quot;&gt;修复&lt;/h2&gt;

&lt;p&gt;前往仓库路径，然后删除这些分支对应的文件。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前往仓库所在的本地文件夹；&lt;/li&gt;
  &lt;li&gt;进入子目录 &lt;code class=&quot;highlighter-rouge&quot;&gt;.git\refs\remotes&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;一个个对着上面失败的分支，将其删除。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-03-11-52-12.png&quot; alt=&quot;删除错误的分支&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如在我的错误例子中，要删除的文件分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.git\refs\remotes\origin\next\release&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.git\refs\remotes\origin\feature\ai&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.git\refs\remotes\origin\release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;随后，重新尝试 &lt;code class=&quot;highlighter-rouge&quot;&gt;git fetch&lt;/code&gt;，git 会重新生成这些分支文件，因此不用担心会删出问题：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git fetch &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prune&lt;/span&gt;
Fetching origin
From gitlab.gz.cvte.cn:t/tech-app/dev/win/app/easinote
   a1fd2551f7..cfb662e870  next/release  -&amp;gt; origin/next/release
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;new branch]            feature/ai    -&amp;gt; origin/feature/ai
   97d72dfc8f..ceb346c8e2  release       -&amp;gt; origin/release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ## 原因

---

**参考资料** --&gt;
</description>
        <pubDate>Thu, 05 Sep 2019 06:37:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-fetch-failed-for-reference-broken.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-fetch-failed-for-reference-broken.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>WPF 不要给 Window 类设置变换矩阵（分析篇）：System.InvalidOperationException: 转换不可逆。</title>
        <description>&lt;p&gt;最近总是收到一个异常 “&lt;code class=&quot;highlighter-rouge&quot;&gt;System.InvalidOperationException: 转换不可逆。&lt;/code&gt;”，然而看其堆栈，一点点自己写的代码都没有。到底哪里除了问题呢？&lt;/p&gt;

&lt;p&gt;虽然异常堆栈信息里面没有自己编写的代码，但是我们还是找到了问题的原因和解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;异常堆栈&quot;&gt;异常堆栈&lt;/h2&gt;

&lt;p&gt;这就是抓到的此问题的异常堆栈：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;转换不可逆。&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryApplyVisualTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryClientToRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MouseDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LocalHitTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enabledHit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalHit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MouseDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GlobalHitTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enabledHit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalHit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusWisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WispStylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusWisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WispLogic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PreNotifyInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotifyInputEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessStagingArea&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusWisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WispLogic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InputManagerProcessInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InternalRealCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryCatchWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;catchHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，我们的堆栈结束点是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionWrapper.TryCatchWhen&lt;/code&gt; 可以得知此异常是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.UnhandledException&lt;/code&gt; 来捕获的。也就是说，此异常直接通过 Windows 消息被我们间接触发，而不是直接通过我们编写的代码触发。而最顶端是对矩阵求逆，而此异常是试图对一个不可逆的矩阵求逆。&lt;/p&gt;

&lt;h2 id=&quot;分析过程&quot;&gt;分析过程&lt;/h2&gt;

&lt;p&gt;如果你不想看分析过程，可以直接移步至本文的最后一节看原因和解决方案。&lt;/p&gt;

&lt;h3 id=&quot;源代码&quot;&gt;源代码&lt;/h3&gt;

&lt;p&gt;因为 .NET Framework 版本的 WPF 是开源的，.NET Core 版本的 WPF 目前还处于按揭开源的状态，所以我们看 .NET Framework 版本的代码来分析原因。&lt;/p&gt;

&lt;p&gt;我按照调用堆栈从顶到底的顺序，将前面三帧的代码贴到下面。&lt;/p&gt;

&lt;h4 id=&quot;pointutiltryapplyvisualtransform&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PointUtil.TryApplyVisualTransform&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryApplyVisualTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetVisualTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasInverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;pointutiltryclienttoroot&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PointUtil.TryClientToRoot&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityTreatAsSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryClientToRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsDisposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TransformFromDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryApplyVisualTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwOnError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能会说，在调用堆栈上面看不到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PointUtil.ClientToRoot&lt;/code&gt; 方法。但其实如果我们看一看 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseDevice.LocalHitTest&lt;/code&gt; 的代码，会发现其实调用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PointUtil.ClientToRoot&lt;/code&gt; 方法。在调用堆栈上面看不到它是因为方法足够简单，被内联了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityTreatAsSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientToRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryClientToRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;求逆的矩阵&quot;&gt;求逆的矩阵&lt;/h3&gt;

&lt;p&gt;下面我们一步一步分析异常的原因。&lt;/p&gt;

&lt;p&gt;我们先看看是什么代码在做矩阵求逆。下面截图中的方法是反编译的，就是上面我们在源代码中列出的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryApplyVisualTransform&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-09-51-48.png&quot; alt=&quot;矩阵求逆的调用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;先获取了传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 对象的变换矩阵，然后根据参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;inverse&lt;/code&gt; 来对其求逆。如果矩阵可以求逆，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;HasInverse&lt;/code&gt; 属性返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么代码可以继续执行下去而不会出现异常。但如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;HasInverse&lt;/code&gt; 返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，则根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;throwOnError&lt;/code&gt; 来决定是否抛出异常，在需要抛出异常的情况下会真实求逆，也就是上面截图中我们看到的异常发生处的代码。&lt;/p&gt;

&lt;p&gt;那么接下来我们需要验证三点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 是哪里来的；&lt;/li&gt;
  &lt;li&gt;这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 的变换矩阵什么情况下不可求逆；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;throwOnError&lt;/code&gt; 确定传入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 吗。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是我们继续往上层调用代码中查看。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-10-00-12.png&quot; alt=&quot;应用变换的调用 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-10-01-40.png&quot; alt=&quot;应用变换的调用 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以很快验证上面需要验证的两个点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;throwOnError&lt;/code&gt; 传入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;RootVisual&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;RootVisual&lt;/code&gt; 是什么呢？&lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 是承载 WPF 可视化树的一个对象，对于窗口 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;，是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 的子类）承载的；对于跨线程 WPF UI，可以通过自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 子类来完成。这部分可以参考我之前的一些博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/multi-thread-ui-using-visualtarget-in-wpf&quot;&gt;WPF 同一窗口内的多线程 UI（VisualTarget）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/embed-win32-window-using-csharp&quot;&gt;WPF 同一窗口内的多线程/多进程 UI（使用 SetParent 嵌入另一个窗口）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-an-async-loading-view&quot;&gt;WPF 多线程 UI：设计一个异步加载 UI 的容器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/get-wpf-visual-scaling-ratio-to-device&quot;&gt;WPF 获取元素（Visual）相对于屏幕设备的缩放比例，可用于清晰显示图片&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不管怎么说，这个指的就是 WPF 可视化树的根：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 来显示 WPF 窗口，那么根就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类；&lt;/li&gt;
  &lt;li&gt;如果你是用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Popup&lt;/code&gt; 来承载一个弹出框，那么根就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PopupRoot&lt;/code&gt; 类；&lt;/li&gt;
  &lt;li&gt;如果你使用了一些跨线程/跨进程 UI 的技术，那么根就是自己写的可视化树根元素。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于绝大多数 WPF 开发者来说，只会碰到前面第一种情况，也就是仅仅有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 作为可视化树的根的情况。一般人很难直接给 &lt;code class=&quot;highlighter-rouge&quot;&gt;PopupRoot&lt;/code&gt; 设置变换矩阵，一般 WPF 程序的代码也很少做跨线程或跨进程 UI。&lt;/p&gt;

&lt;p&gt;于是我们几乎可以肯定，是有某处的代码让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的变换矩阵不可逆了。&lt;/p&gt;

&lt;h3 id=&quot;矩阵求逆&quot;&gt;矩阵求逆&lt;/h3&gt;

&lt;p&gt;什么样的矩阵是不可逆的？&lt;/p&gt;

&lt;h4 id=&quot;异常代码&quot;&gt;异常代码&lt;/h4&gt;

&lt;p&gt;发生异常的代码是 WPF 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix.Invert&lt;/code&gt; 方法，其发生异常的代码如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-10-15-07.png&quot; alt=&quot;Matrix.Invert&quot; /&gt;&lt;/p&gt;

&lt;p&gt;首先判断矩阵的行列式 &lt;code class=&quot;highlighter-rouge&quot;&gt;Determinant&lt;/code&gt; 是否为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，如果为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 则抛出矩阵不可逆的异常。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-10-15-14.png&quot; alt=&quot;Matrix.Determinant&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;行列式&quot;&gt;行列式&lt;/h4&gt;

&lt;p&gt;WPF 的 2D 变换矩阵 \(M\) 是一个 \(3\times{3}\) 的矩阵：&lt;/p&gt;

\[\begin{bmatrix}
M11 &amp;amp; M12 &amp;amp; 0 \\
M21 &amp;amp; M22 &amp;amp; 0 \\
OffsetX &amp;amp; OffsetY &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;其行列式 \(det(M)\) 是一个标量：&lt;/p&gt;

\[\left | A \right | =
\begin{vmatrix}
M11 &amp;amp; M12 &amp;amp; 0 \\
M21 &amp;amp; M22 &amp;amp; 0 \\
OffsetX &amp;amp; OffsetY &amp;amp; 1
\end{vmatrix}
= M11 \times M22 - M12 \times M21\]

&lt;p&gt;因为矩阵求逆的时候，行列式的值会作为分母，于是会无法计算，所以行列式的值为 0 时，矩阵不可逆。&lt;/p&gt;

&lt;p&gt;前面我们计算 WPF 的 2D 变换矩阵的行列式的值为 \(M11 \times M22 - M12 \times M21\)，因此，只要使这个式子的值为 0 即可。&lt;/p&gt;

&lt;p&gt;那么 WPF 的 2D 变换的时候，如何使此值为 0 呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;平移？平移只会修改 \(OffsetX\) 和 \(OffsetY\)，因此对结果没有影响&lt;/li&gt;
  &lt;li&gt;缩放？缩放会将原矩阵点乘缩放矩阵&lt;/li&gt;
  &lt;li&gt;旋转？旋转会将旋转矩阵点乘原矩阵&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，原矩阵在我们的场景下就是恒等的矩阵，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix.Identity&lt;/code&gt;：&lt;/p&gt;

\[\begin{bmatrix}
1 &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; 1 &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;接下来缩放和旋转我们都不考虑变换中心的问题，因为变换中心的问题都可以等价为先进行缩放和旋转后，再单纯进行平移。由于平移对行列式的值没有影响，于是我们忽略。&lt;/p&gt;

&lt;h4 id=&quot;缩放矩阵&quot;&gt;缩放矩阵&lt;/h4&gt;

&lt;p&gt;缩放矩阵。如果水平和垂直分量分别缩放 \(ScaleX\) 和 \(ScaleY\) 倍，则缩放矩阵为：&lt;/p&gt;

\[\begin{bmatrix}
ScaleX &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; ScaleY &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;原矩阵点乘缩放矩阵结果为：&lt;/p&gt;

\[\begin{bmatrix}
1 &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; 1 &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}
\cdot \begin{bmatrix}
ScaleX &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; ScaleY &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}
= \begin{bmatrix}
ScaleX &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; ScaleY &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;于是，只要 \(ScaleX\) 和 \(ScaleY\) 任何一个为 0 就可以导致新矩阵的行列式必定为 0。&lt;/p&gt;

&lt;h4 id=&quot;旋转矩阵&quot;&gt;旋转矩阵&lt;/h4&gt;

&lt;p&gt;旋转矩阵。假设用户设置的旋转角度为 &lt;code class=&quot;highlighter-rouge&quot;&gt;angle&lt;/code&gt;，那么换算成弧度为 &lt;code class=&quot;highlighter-rouge&quot;&gt;angle * (Math.PI/180.0)&lt;/code&gt;，我们将弧度记为 \(\alpha\)，那么旋转矩阵为：&lt;/p&gt;

\[\begin{bmatrix}
\cos{\alpha} &amp;amp; \sin{\alpha} &amp;amp; 0 \\
-\sin{\alpha} &amp;amp; \cos{\alpha} &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;旋转矩阵点乘原矩阵的结果为：&lt;/p&gt;

\[\begin{bmatrix}
\cos{\alpha} &amp;amp; \sin{\alpha} &amp;amp; 0 \\
-\sin{\alpha} &amp;amp; \cos{\alpha} &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}
\cdot \begin{bmatrix}
1 &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; 1 &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}
= \begin{bmatrix}
\cos{\alpha} &amp;amp; \sin{\alpha} &amp;amp; 0 \\
-\sin{\alpha} &amp;amp; \cos{\alpha} &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1
\end{bmatrix}\]

&lt;p&gt;对此矩阵的行列式求值：&lt;/p&gt;

\[\cos^{2}{\alpha} + \sin^{2}{\alpha} = 1\]

&lt;p&gt;也就是说其行列式的值恒等于 1，因此其矩阵必然可求逆。&lt;/p&gt;

&lt;h4 id=&quot;wpf-2d-变换矩阵求逆小结&quot;&gt;WPF 2D 变换矩阵求逆小结&lt;/h4&gt;

&lt;p&gt;对于 WPF 的 2D 变换矩阵：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;平移和旋转不可能导致矩阵不可逆；&lt;/li&gt;
  &lt;li&gt;缩放，只要水平和垂直方向的任何一个分量缩放量为 0，矩阵就会不可逆。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;寻找问题代码&quot;&gt;寻找问题代码&lt;/h3&gt;

&lt;p&gt;现在，我们寻找问题的方向已经非常明确了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;找到设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;，检查其是否给 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleX&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleY&lt;/code&gt; 属性赋值为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而，真正写一个 demo 程序来验证这个问题的时候，就发现没有这么简单。因为：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-11-40-39.png&quot; alt=&quot;不能给 Window 设置变换矩阵&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们发现，不止是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleX&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleY&lt;/code&gt; 属性不能设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，实际上设成 &lt;code class=&quot;highlighter-rouge&quot;&gt;0.5&lt;/code&gt; 或者其他值也是不行的。&lt;/p&gt;

&lt;p&gt;唯一合理值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;那么为什么依然有异常呢？难道是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt; 的值一开始正常，然后被修改？&lt;/p&gt;

&lt;p&gt;编写 demo 验证，果然如此。而只有变换到 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 才会真的引发本文一开始我们提到的异常。一般会开始设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 而后设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 的代码通常是在做动画。&lt;/p&gt;

&lt;p&gt;一定是有代码正在为窗口的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt; 做动画。&lt;/p&gt;

&lt;p&gt;结果全代码仓库搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt; 真的找到了问题代码。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDoubiWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Exceptions.Unknown.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MainWindow&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.RenderTransform&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ScaleTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.RenderTransform&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.Load&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDoubiWindow&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(UIElement.RenderTransform).(ScaleTransform.ScaleX)&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDoubiWindow&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(UIElement.RenderTransform).(ScaleTransform.ScaleY)&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略的代码 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，这段代码并不会导致每次都出现异常，而是在非常多次尝试中偶尔能出现一次异常。&lt;/p&gt;

&lt;h2 id=&quot;原因和解决方案&quot;&gt;原因和解决方案&lt;/h2&gt;

&lt;h3 id=&quot;原因&quot;&gt;原因&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类是不可以设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransform&lt;/code&gt; 属性的，但允许设置恒等（&lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix.Identity&lt;/code&gt;）的变换；&lt;/li&gt;
  &lt;li&gt;如果让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类缩放分量设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，就会出现矩阵不可逆异常。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;解决方案&quot;&gt;解决方案&lt;/h3&gt;

&lt;p&gt;不要给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类设置变换，如果要做，请给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 内部的子元素设置。比如上面的例子中，我们给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 设置就没有问题（而且可以做到类似的效果。&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Sep 2019 06:13:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/analyze-matrix-invert-exception-for-wpf-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/analyze-matrix-invert-exception-for-wpf-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 不要给 Window 类设置变换矩阵（应用篇）</title>
        <description>&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类是不允许设置变换矩阵的。不过，总会有小伙伴为了能够设置一下试图绕过一些验证机制。&lt;/p&gt;

&lt;p&gt;不要试图绕过，因为你会遇到更多问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;试图设置变换矩阵&quot;&gt;试图设置变换矩阵&lt;/h2&gt;

&lt;p&gt;当你试图给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类设置变换矩阵的时候，会出现异常：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.InvalidOperationException:“转换对于 Window 无效。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;无论是缩放还是旋转，都一样会出现异常。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-11-58-46.png&quot; alt=&quot;转换对于 Window 无效 - 缩放&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-09-02-12-23-55.png&quot; alt=&quot;转换对于 Window 无效 - 旋转&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们在 &lt;a href=&quot;/post/analyze-matrix-invert-exception-for-wpf-window&quot;&gt;WPF 不要给 Window 类设置变换矩阵（分析篇）&lt;/a&gt; 一文中已经证明在 WPF 的 2D 变换中，旋转一定不会造成矩阵不可逆，因此此验证是针对此属性的强验证。&lt;/p&gt;

&lt;p&gt;只有做设置的变换是恒等变换的时候，才可以完成设置。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ScaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RotateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MatrixTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;绕过验证&quot;&gt;绕过验证&lt;/h2&gt;

&lt;p&gt;然而你可以通过先设置变换，再修改变换值的方式绕过验证：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ScaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScaleX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScaleY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上，你绕过也没有关系，可是这样的设置实际上是没有任何效果的。&lt;/p&gt;

&lt;p&gt;不过为什么还是会有小伙伴这么设置呢？&lt;/p&gt;

&lt;p&gt;是因为小伙伴同时还设置了窗口透明 &lt;code class=&quot;highlighter-rouge&quot;&gt;AllowsTransparency=&quot;True&quot;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle=&quot;None&quot;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Background=&quot;Transparent&quot;&lt;/code&gt;，导致看起来好像这个变换生效了一样。&lt;/p&gt;

&lt;h2 id=&quot;小心异常&quot;&gt;小心异常&lt;/h2&gt;

&lt;p&gt;此设置不仅没有效果，还会引发异常，请阅读我的另一篇博客了解：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analyze-matrix-invert-exception-for-wpf-window&quot;&gt;WPF 不要给 Window 类设置变换矩阵（分析篇）：System.InvalidOperationException: 转换不可逆。&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 02 Sep 2019 04:34:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dont-set-or-animate-scale-transform-for-a-wpf-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dont-set-or-animate-scale-transform-for-a-wpf-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>通过设置启用 Visual Studio 默认关闭的大量强大的功能提升开发效率</title>
        <description>&lt;p&gt;使用 Visual Studio 开发 C#/.NET 应用程序，以前有 ReSharper 来不足其各项功能短板，后来不断将 ReSharper 的功能一点点搬过来稍微好了一些。不过直到 Visual Studio 2019，才开始渐渐可以和 ReSharper 拼一下了。&lt;/p&gt;

&lt;p&gt;如果你使用 Visual Studio 2019，那么像本文这样配置一下，可以大大提升你的开发效率。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;工具选项&quot;&gt;工具选项&lt;/h2&gt;

&lt;p&gt;打开菜单 “工具” -&amp;gt; “选项”，然后你就打开了 Visual Studio 的选项窗口。接下来本文的所有内容都会在这里进行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-20-31-44.png&quot; alt=&quot;打开选项窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;文本编辑器&quot;&gt;文本编辑器&lt;/h2&gt;

&lt;p&gt;在 “文本编辑器” -&amp;gt; “常规” 分类中，我们关心这些设置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;使鼠标单击可执行转到定义&lt;/code&gt; &lt;em&gt;这样按住 Ctrl 键点击标识符的时候可以转到定义（开启此选项之后，后面有其他选项可以转到反编译后的源码）&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-20-35-06.png&quot; alt=&quot;文本编辑器 -&amp;gt; 常规&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然也有其他可以打开玩的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;查看空白&lt;/code&gt; &lt;em&gt;专治强迫症，可以把空白字符都显示出来，这样你可以轻易看到对齐问题以及多于的空格了&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c&quot;&gt;C#&lt;/h2&gt;

&lt;p&gt;在 “文本编辑器” -&amp;gt; “C#” -&amp;gt; “IntelliSense” 分类中，我们关心这些设置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;键入字符后显示完成列表&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;删除字符后显示完成列表&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;突出显示完成列表项的匹配部分&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;显示完成项筛选器&lt;/code&gt; &lt;em&gt;打开这些选项可以让智能感知列表更容易显示出来，而我们也知道智能感知列表的强大&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;推荐&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;显示 unimported 命名空间中的项(实验)&lt;/code&gt; &lt;em&gt;这一项默认不会勾选，但强烈建议勾选上；它可以帮助我们直接输入没有 using 的命名空间中的类型，这可以避免记住大量记不住的类名&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-04-52.png&quot; alt=&quot;IntelliSense&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;c-高级&quot;&gt;C# 高级&lt;/h2&gt;

&lt;p&gt;在 “文本编辑器” -&amp;gt; “C#” -&amp;gt; “高级” 分类中，我们关心大量设置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;支持导航到反编译源(实验)&lt;/code&gt; &lt;em&gt;前面我们说可以 Ctrl + 鼠标导航到定义，如果打开了这个就可以看反编译后的源码了&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;启用可为 null 的引用分析 IDE 功能&lt;/code&gt; &lt;em&gt;这个功能可能还没有完成，暂时还是无法开启的&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-09-20.png&quot; alt=&quot;高级&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然也有其他可以打开玩的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;启用完成解决方案分析&lt;/code&gt; &lt;em&gt;这是基于 Roslyn 的分析，Visual Studio 的大量重构功能都依赖于它；默认关闭也可以用，只是仅分析当前正在编辑的文件；如果打开则分析整个解决方案，你会在错误列表中看到大量的编译警告&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;代码样式&quot;&gt;代码样式&lt;/h2&gt;

&lt;p&gt;在 “文本编辑器” -&amp;gt; “C#” -&amp;gt; “代码样式” 分类，如果你关心代码的书写风格，那么这个分类底下的每一个子类别都可以考虑一个个检查一下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-23-37.png&quot; alt=&quot;代码样式&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;人工智能-intellicode&quot;&gt;人工智能 IntelliCode&lt;/h2&gt;

&lt;p&gt;Visual Studio 2019 默认安装了 IntelliCode 可以充分利用微软使用 GitHub 上开源项目训练出来的模型来帮助编写代码。这些强烈建议开启。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C# 基础模型&lt;/code&gt; &lt;em&gt;微软利用 GitHub 开源项目训练的基础模型&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;XAML 基础模型&lt;/code&gt; &lt;em&gt;微软利用 GitHub 开源项目训练的基础模型&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C# 参数完成&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C# 自定义模型&lt;/code&gt; &lt;em&gt;如果针对单个项目训练出来了模型，那么可以使用专门针对此项目训练的模型&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EditorConfig 推理&lt;/code&gt; &lt;em&gt;可以根据项目推断生成 EditorConfig 文件&lt;/em&gt; 可以参见&lt;a href=&quot;/post/editor-config-for-visual-studio&quot;&gt;在 Visual Studio 中使用 EditorConfig 统一代码风格&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;自定义模型训练提示&lt;/code&gt; &lt;em&gt;如果开启，那么每个项目的规模如果达到一定程度就会提示训练一个自定义模型出来&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-26-01.png&quot; alt=&quot;IntelliCode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://docs.microsoft.com/en-us/visualstudio/intellicode/media/intellicode-options.png&quot; alt=&quot;IntelliCode English&quot; /&gt;&lt;/p&gt;

&lt;p&gt;训练模型会上传一部分数据到 IntelliCode 服务器，你可以去 &lt;code class=&quot;highlighter-rouge&quot;&gt;%TEMP%\Visual Studio IntelliCode&lt;/code&gt; 目录来查看到底上传了哪些数据。&lt;/p&gt;

&lt;h2 id=&quot;快捷键&quot;&gt;快捷键&lt;/h2&gt;

&lt;p&gt;当然，设置好快捷键也是高效编码的重要一步，可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/customizing-keyboard-shortcuts-in-visual-studio&quot;&gt;如何快速自定义 Visual Studio 中部分功能的快捷键&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/keyboard-shortcuts-to-improve-the-efficiency-of-visual-studio&quot;&gt;提高使用 Visual Studio 开发效率的键盘快捷键&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;自动完成&quot;&gt;自动完成&lt;/h2&gt;

&lt;p&gt;在你点击 “确定” 关闭了以上窗口之后，我们还需要设置一项。&lt;/p&gt;

&lt;p&gt;确保下图中的这个按钮处于 “非选中” 状态：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-49-58.png&quot; alt=&quot;建议完成模式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样，当出现智能感知列表的时候，我们直接就可以按下回车键输入这个选项了；否则你还需要按上下选中再回车。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-29-21-53-51.png&quot; alt=&quot;建议完成和标准完成&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 29 Aug 2019 13:54:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/let-visual-studio-empower-more-by-change-some-settings.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/let-visual-studio-empower-more-by-change-some-settings.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议</title>
        <description>&lt;p&gt;在 C#/.NET 中，有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(0)&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Yield()&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 中，有几种不同的让当前线程释放执行权的方法。他们的作用都是放弃当前线程当前的执行权，让其他线程得以调度。但是他们又不太一样。&lt;/p&gt;

&lt;p&gt;本文说说他们的原理区别和用法区别。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;原理区别&quot;&gt;原理区别&lt;/h2&gt;

&lt;h3 id=&quot;threadsleep0&quot;&gt;Thread.Sleep(0)&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(int millisecondsTimeout)&lt;/code&gt; 的代码贴在下面，其内部实际上是在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SleepInternal&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;SleepInternal&lt;/code&gt; 由 CLR 内部实现。其目的是将当前线程挂起一个指定的时间间隔。&lt;/p&gt;

&lt;p&gt;如果将超时时间设置为 0，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt;，那么这将强制当前线程放弃剩余的 CPU 时间片。&lt;/p&gt;

&lt;p&gt;放弃当前线程剩余的 CPU 时间片就意味着其他比此线程优先级高且可以被调度的线程将会在此时被调度。然而此方法只是放弃当前 CPU 执行的时间片，如果当前系统环境下其他可以被调度的其他线程的优先级都比这个线程的优先级低，实际上此线程依然还是会优先执行。&lt;/p&gt;

&lt;p&gt;如果你的方法不会被其他线程影响，那么不会有执行上的区别，但如果你的方法涉及到多个线程的调用，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt; 的调用可能导致其他线程也进入此方法（而不是等此线程的当前时间片执行完后再进入）。当然，CPU 对单个线程的执行时间片是纳秒级别的，所以实际上你因为此方法调用获得的多线程重入效果是“纯属巧合”的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*=========================================================================
** Suspends the current thread for timeout milliseconds. If timeout == 0,
** forces the thread to give up the remainer of its timeslice.  If timeout
** == Timeout.Infinite, no timeout will occur.
**
** Exceptions: ArgumentException if timeout &amp;lt; 0.
**             ThreadInterruptedException if the thread is interrupted while sleeping.
=========================================================================*/&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// auto-generated&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResourceExposure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResourceScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MethodImplAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MethodImplOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InternalCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SleepInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;millisecondsTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// auto-generated&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;millisecondsTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SleepInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;millisecondsTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Ensure we don't return to app code when the pause is underway&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppDomainPauseManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsPaused&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AppDomainPauseManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResumeEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOneWithoutFAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;threadyield&quot;&gt;Thread.Yield()&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Yield()&lt;/code&gt; 的代码贴在下面，其内部调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;YieldInternal&lt;/code&gt;，实际上也是由 CLR 内部实现。&lt;/p&gt;

&lt;p&gt;此方法也是放弃当前线程的剩余时间片，所以其效果与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt; 是相同的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// auto-generated&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResourceExposure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResourceScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JitHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SuppressUnmanagedCodeSecurity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HostProtection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Synchronization&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExternalThreading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ReliabilityContract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consistency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WillNotCorruptState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;YieldInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// auto-generated&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HostProtection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Synchronization&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExternalThreading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ReliabilityContract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consistency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WillNotCorruptState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;YieldInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;threadsleep1&quot;&gt;Thread.Sleep(1)&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(1)&lt;/code&gt; 与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt; 虽然只有参数上的微小差别，但实际上做了不同的事情。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(1)&lt;/code&gt; 会使得当前线程挂起一个指定的超时时间，这里设置为 1ms。于是，在这个等待的超时时间段内，你的当前线程处于不可被调度的状态。那么即便当前剩余的可以被调度的线程其优先级比这个更低，也可以得到调度。&lt;/p&gt;

&lt;p&gt;下面是针对这三个方法执行时间的一个实验结果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-27-11-10-43.png&quot; alt=&quot;Thread 不同方法的耗时实验结果&quot; /&gt;&lt;br /&gt;
▲ Thread 不同方法的耗时实验结果&lt;/p&gt;

&lt;p&gt;其中，Nothing 表示没有写任何代码。&lt;/p&gt;

&lt;p&gt;测量使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt;，你可以通过阅读 &lt;a href=&quot;/post/dotnet-high-precision-performance-counting&quot;&gt;.NET/C# 在代码中测量代码执行耗时的建议（比较系统性能计数器和系统时间）&lt;/a&gt; 了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; 测量的原理和精度。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartNew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Thread.Sleep(0) : &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;taskdelay0&quot;&gt;Task.Delay(0)&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 系列的线程模型（TAP）中的方法。关于 TAP 可参见 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?wt.mc_id=MVP&quot;&gt;Task-based Asynchronous Pattern (TAP)  Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这是一套基于异步状态机（AsyncStateMachine）实现的线程模型，这也是与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread&lt;/code&gt; 系列方法最大的不同。&lt;/p&gt;

&lt;p&gt;当传入参数 0 的时候，会直接返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.CompletedTask&lt;/code&gt;。这意味着你在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(0)&lt;/code&gt; 后面写的代码会被立刻调用（如果还有剩余 CPU 时间片的话）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Creates a Task that will complete after a time delay.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;millisecondsDelay&quot;&amp;gt;The number of milliseconds to wait before completing the returned Task&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;cancellationToken&quot;&amp;gt;The cancellation token that will be checked prior to completing the returned Task&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;A Task that represents the time delay&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;exception cref=&quot;T:System.ArgumentOutOfRangeException&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// The &amp;lt;paramref name=&quot;millisecondsDelay&quot;/&amp;gt; is less than -1.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/exception&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;exception cref=&quot;T:System.ObjectDisposedException&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// The provided &amp;lt;paramref name=&quot;cancellationToken&quot;/&amp;gt; has already been disposed.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/exception&amp;gt;        &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// If the cancellation token is signaled before the specified time delay, then the Task is completed in&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Canceled state.  Otherwise, the Task is completed in RanToCompletion state once the specified time&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// delay has expired.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;        &lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;millisecondsDelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Throw on non-sensical time&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;millisecondsDelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;millisecondsDelay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetResourceString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Task_Delay_InvalidMillisecondsDelay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// some short-cuts in case quick completion is in order&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCancellationRequested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// return a Task created as already-Canceled&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromCancellation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;millisecondsDelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// return a Task created as already-RanToCompletion&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompletedTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Construct a promise-style Task to encapsulate our return value&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DelayPromise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Register our cancellation token, if necessary.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeCanceled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Registration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InternalRegisterWithoutEC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DelayPromise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ... and create our timer and make sure that it stays rooted.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;millisecondsDelay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Infinite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// no need to create the timer if it's an infinite timeout&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DelayPromise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;millisecondsDelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Infinite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;KeepRootedWhileScheduled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Return the timer proxy task&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;taskyield&quot;&gt;Task.Yield()&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 的最大作用实际上是让一个异步方法立刻返回，让后面其他代码的调用进入下一个异步上下文。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 执行某些操作。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 执行另一些操作。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果外面的代码使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 来等待 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield&lt;/code&gt; 的作用可能不太明显，但是如果外面并没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 或者任何一层更外层的调用没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;，那么就有区别了。对于没有异步等待的调用，那个方法就会在此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 这一句执行后返回。而此后的代码将在那些没有异步等待的方法之后执行。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 实际上只是返回一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;YieldAwaitable&lt;/code&gt; 的新实例，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;YieldAwaitable.GetAwaiter&lt;/code&gt; 方法返回一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;YieldAwaiter&lt;/code&gt; 的新实例。也就是说，后续的执行效果完全取决于 &lt;code class=&quot;highlighter-rouge&quot;&gt;YieldAwaiter&lt;/code&gt; 是如何实现这个异步过程的（异步状态机会执行这个过程）。我有另一篇博客说明 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt; 是如何实现的：&lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;如何实现一个可以用 await 异步等待的 Awaiter&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;YieldAwaiter&lt;/code&gt; 靠 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueueContinuation&lt;/code&gt; 来决定后续代码的执行时机。此方法的核心代码贴在了下面。&lt;/p&gt;

&lt;p&gt;有两个分支，如果指定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;，那么就会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Post&lt;/code&gt; 方法来执行异步任务的下一个步骤。调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;continuation&lt;/code&gt; 就是执行异步状态机中的下一个步骤以进入下一个异步状态；不过，为了简化理解，你可以认为这就是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 后面的那段代码。&lt;/p&gt;

&lt;p&gt;WPF UI 线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 被设置为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt;，它的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Post&lt;/code&gt; 方法本质上是用消息循环来实现的。其他线程如果没有特殊设置，则是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。这一部分知识可以看参见：&lt;a href=&quot;/post/yield-in-task-dispatcher&quot;&gt;出让执行权：Task.Yield, Dispatcher.Yield&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果没有指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 或者当前的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 类型基类，那么就会执行后面 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 中的逻辑。主要就是在线程池中寻找一个线程然后执行代码，或者再次启动一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 任务并加入队列；这取决于 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskScheduler.Current&lt;/code&gt; 的设置。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Get the current SynchronizationContext, and if there is one,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// post the continuation to it.  However, treat the base type&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// as if there wasn't a SynchronizationContext, since that's what it&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// logically represents.&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syncCtx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentNoFlow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syncCtx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syncCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;syncCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s_sendOrPostCallbackRunAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If we're targeting the default scheduler, queue to the thread pool, so that we go into the global&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// queue.  As we're going into the global queue, we might as well use QUWI, which for the global queue is&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// just a tad faster than task, due to a smaller object getting allocated and less work on the execution path.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TaskScheduler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scheduler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheduler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flowContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ThreadPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;QueueUserWorkItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s_waitCallbackRunAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ThreadPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnsafeQueueUserWorkItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s_waitCallbackRunAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We're targeting a custom scheduler, so queue a task.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartNew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskCreationOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PreferFairness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;taskdelay1&quot;&gt;Task.Delay(1)&lt;/h3&gt;

&lt;p&gt;与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread&lt;/code&gt; 一样，&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(1)&lt;/code&gt; 与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(0)&lt;/code&gt; 虽然只有参数上的微小差别，但实际上也做了不同的事情。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(1)&lt;/code&gt; 实际上是启动了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Threading.Timer&lt;/code&gt;，然后订阅时间抵达之后的回调函数。&lt;/p&gt;

&lt;p&gt;会从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Timer.TimerSetup&lt;/code&gt; 设置，到使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TimerHolder&lt;/code&gt; 并在内部使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TimerQueueTimer&lt;/code&gt; 来设置回调；内部实际使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TimerQueue.UpdateTimer&lt;/code&gt; 来完成时间等待之后的回调通知，最终通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnsureAppDomainTimerFiresBy&lt;/code&gt; 调用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ChangeAppDomainTimer&lt;/code&gt; 来完成时间抵达之后的回调。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 之后的那段代码会被异步状态机封装，传入上面的回调中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResourceExposure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResourceScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JitHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SuppressUnmanagedCodeSecurity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChangeAppDomainTimer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppDomainTimerSafeHandle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dueTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;相比于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread&lt;/code&gt; 相关方法仅涉及到当前线程的调度，&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 相关的方法会涉及到线程池的调度，并且使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Threading.Timer&lt;/code&gt; 来进行计时，耗时更加不可控：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-27-12-51-30.png&quot; alt=&quot;Task 不同方法的耗时实验结果&quot; /&gt;&lt;br /&gt;
▲ Task 不同方法的耗时实验结果（三次不同的实验结果）&lt;/p&gt;

&lt;p&gt;其中，Nothing 表示没有写任何代码。&lt;/p&gt;

&lt;p&gt;测量使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt;，你依然可以通过阅读 &lt;a href=&quot;/post/dotnet-high-precision-performance-counting&quot;&gt;.NET/C# 在代码中测量代码执行耗时的建议（比较系统性能计数器和系统时间）&lt;/a&gt; 了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; 测量的原理和精度。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartNew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Thread.Sleep(0) : &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 [c# - Task.Delay(&lt;ms&gt;).Wait(); sometimes causing a 15ms delay in messaging system - Stack Overflow](https://stackoverflow.com/q/41830216/6233938) 上有个说法，说操作系统的时钟中断有时间间隔，而 Windows 操作系统上这个时间间隔的默认值大约在 15ms，所以实际上你写等待 1ms，实际等待时间也会接近 15ms。&lt;/ms&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re seeing an artifact of the Windows interrupt rate, which is (by default) approx every 15ms. Thus if you ask for 1-15ms, you’ll get an approx 15ms delay. ~16-30 will yield 30ms… so on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;用法区别&quot;&gt;用法区别&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Yield&lt;/code&gt; 在线程调度的效果上是相同的，&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(int)&lt;/code&gt; 是带有超时的等待，本质上也是线程调度。如果你希望通过放弃当前线程时间片以便给其他线程一些执行实际，那么考虑 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(0)&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Yield&lt;/code&gt;；如果希望进行线程调度级别的等待（效果类似于阻塞线程），那么使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(int)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果你允许有一个异步上下文，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async/await&lt;/code&gt;，那么可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(0)&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt;。另外，如果等待时使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay&lt;/code&gt; 而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep&lt;/code&gt;，那么你可以节省一个线程的资源，尤其是在一个线程池的线程中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sleep&lt;/code&gt; 的话，会使得线程池中更多的线程被进行无意义的占用，对其他任务在线程池中的调度不利。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/stg609/p/3857242.html&quot;&gt;Thread.Sleep(0) vs Sleep(1) vs Yeild - stg609 - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[c# - Task.Delay(&lt;ms&gt;).Wait(); sometimes causing a 15ms delay in messaging system - Stack Overflow](https://stackoverflow.com/q/41830216/6233938)&lt;/ms&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/20082221/6233938&quot;&gt;c# - When to use Task.Delay, when to use Thread.Sleep? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/29356139/6233938&quot;&gt;c# - Should I always use Task.Delay instead of Thread.Sleep? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/d7071ba4-8962-43c6-975a-28cdbce51548/whats-the-difference-between-threadsleep0-and-threadyield?forum=csharplanguage&quot;&gt;What’s the difference between Thread.Sleep(0) and Thread,Yield()?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 29 Aug 2019 08:34:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/sleep-delay-zero-vs-yield.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/sleep-delay-zero-vs-yield.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 的 Application.Current.Dispatcher 中，为什么 Current 可能为 null</title>
        <description>&lt;p&gt;在 WPF 程序中，可能会存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current.Dispatcher.Xxx&lt;/code&gt; 这样的代码让一部分逻辑回到主 UI 线程。因为发现在调用这句代码的时候出现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;，于是就有三位小伙伴告诉我说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Current&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性都可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然而实际上这里只可能 &lt;code class=&quot;highlighter-rouge&quot;&gt;Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 而此上下文的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 是绝对不会为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。（当然我们这里讨论的是常规编程手段，如果非常规手段，你甚至可以让实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 呢……）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;当你的应用程序退出时，所有 UI 线程的代码都不再会执行，因此这是安全的；但所有非 UI 线程的代码依然在继续执行，此时随时可能遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 属性为 null。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;由于本文所述的两个部分都略长，所以拆分成两篇博客，这样更容易理解。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-dispatcher-will-never-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，Dispatcher 属性一定不会为 null&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-current-may-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，为什么 Current 可能为 null&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;applicationcurrent-静态属性&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 静态属性&lt;/h2&gt;

&lt;h3 id=&quot;源代码&quot;&gt;源代码&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类型的源代码会非常长，所以这里就不贴了，可以前往这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherObject.cs&quot;&gt;DispatcherObject.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Current&lt;/code&gt; 返回的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 的静态字段。因此 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 字段为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的时机就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的时机。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     The Current property enables the developer to always get to the application in&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     AppDomain in which they are running.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// There is no need to take the _globalLock because reading a&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// reference is an atomic operation. Moreover taking a lock&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// also causes risk of re-entrancy because it pumps messages.&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_appInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 字段是私有字段，所以仅需调查这个类本身即可找到所有的赋值时机。（反射等非常规手段需要排除在外，因为这意味着开发者是逗比——自己闯的祸不能怪 WPF 框架。）&lt;/p&gt;

&lt;h3 id=&quot;赋值时机&quot;&gt;赋值时机&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 的赋值时机有两处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 的实例构造函数（注意哦，是实例构造函数而不是静态构造函数）；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.DoShutdown&lt;/code&gt; 方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 的实例构造函数中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 的赋值是线程安全的，这意味着多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 实例的构造不会因为线程安全问题导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 字段的状态不正确。&lt;/li&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appCreatedInThisAppDomain&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 那么，将抛出异常，组织此应用程序域中创建第二个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类型的实例。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Application constructor&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///    Critical: This code posts a work item to start dispatcher if in the browser&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///    PublicOk: It is ok because the call itself is not exposed and the application object does this internally.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_globalLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// set the default statics&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// DO NOT move this from the begining of this constructor&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_appCreatedInThisAppDomain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_appInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_appInstance must be null here.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_appInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsShuttingDown&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_appCreatedInThisAppDomain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//lock will be released, so no worries about throwing an exception inside the lock&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SRID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultiSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，此类型实际上是设计为单例的。在第一个实例构造出来之后，单例的实例即可开始使用。&lt;/p&gt;

&lt;h3 id=&quot;后续赋值&quot;&gt;后续赋值&lt;/h3&gt;

&lt;p&gt;此单例实例的唯一结束时机就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.DoShutdown&lt;/code&gt; 方法。这是唯一将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_appInstance&lt;/code&gt; 赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// DO NOT USE - internal method&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&amp;lt;SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Critical: Calls critical code: Window.InternalClose&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Critical: Calls critical code: HwndSource.Dispose&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Critical: Calls critical code: PreloadedPackages.Clear()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&amp;lt;/SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoShutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Event handler exception continuality: if exception occurs in ShuttingDown event handler,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// our cleanup action is to finish Shuttingdown.  Since Shuttingdown cannot be cancelled.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We don't want user to use throw exception and catch it to cancel Shuttingdown.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// fire Applicaiton Exit event&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;OnExit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;SetExitCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_exitCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// By default statics are shared across appdomains, so need to clear&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_globalLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_appInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_mainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_htProps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NonAppWindowsInternal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以调用到此代码的公共 API 有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Shutdown&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 关闭的若干方法（&lt;code class=&quot;highlighter-rouge&quot;&gt;InternalDispose&lt;/code&gt;）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IBrowserHostServices.PostShutdown&lt;/code&gt; 接口方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此，所有直接或间接调用到以上方法的地方都会导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 属性被赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;对所写代码的影响&quot;&gt;对所写代码的影响&lt;/h3&gt;

&lt;p&gt;从以上的分析可以得知，只要你还能在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.DoShutdown&lt;/code&gt; 执行之后继续执行代码，那么这部分的代码都将面临着 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 风险。&lt;/p&gt;

&lt;p&gt;那么，到底有哪些时机可能遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 呢？这部分就与读者项目中所用的业务代码强相关了。&lt;/p&gt;

&lt;p&gt;但是这部分业务代码会有一些公共特征帮助你判定你是否可能写出遭遇 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的代码。&lt;/p&gt;

&lt;p&gt;此特征是：&lt;strong&gt;此代码与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 不在同一线程&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;与-applicationcurrent-不在同一线程&quot;&gt;与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 不在同一线程&lt;/h2&gt;

&lt;p&gt;对于 WPF 程序，你的多数代码可能是由用户交互产生，即便有后续代码的执行，也依然是从 UI 交互产生。这样的代码不会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的情况。&lt;/p&gt;

&lt;p&gt;但是，如果你的代码由非 UI 线程触发，例如在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Usb&lt;/code&gt; 设备改变、与其他端的通信、某些异步代码的回调等等，这些代码不受 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 是否调度影响，几乎一定会执行。因此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 就算赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 了，它们也不知道，依然会继续执行，于是就会遭遇 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这本质上是一个线程安全问题。&lt;/p&gt;

&lt;h4 id=&quot;使用-invokebegininvokeinvokeasync-的代码不会出问题&quot;&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/BeginInvoke/InvokeAsync&lt;/code&gt; 的代码不会出问题&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.DoShutdown&lt;/code&gt; 方法被 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShutdownImpl&lt;/code&gt; 包装，且所有调用均从此包装进入，因此，所有可能导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的代码，均会调用此方法，也就是说，会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.CriticalInvokeShutdown&lt;/code&gt; 实例方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// This method gets called on dispatch of the Shutdown DispatcherOperationCallback&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&amp;lt;SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///  Critical: Calls critical code: DoShutdown, Dispatcher.CritcalInvokeShutdown()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///&amp;lt;/SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShutdownImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Event handler exception continuality: if exception occurs in Exit event handler,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// our cleanup action is to finish Shutdown since Exit cannot be cancelled. We don't&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// want user to use throw exception and catch it to cancel Shutdown.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoShutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Quit the dispatcher if we ran our own.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_ownDispatcherStarted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CriticalInvokeShutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;ServiceProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所有的关闭 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的调用有两类，&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 关闭时调用的是内部方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;CriticalInvokeShutdown&lt;/code&gt;。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;立即关闭 &lt;code class=&quot;highlighter-rouge&quot;&gt;CriticalInvokeShutdown&lt;/code&gt;，即以 &lt;code class=&quot;highlighter-rouge&quot;&gt;Send&lt;/code&gt; 优先级 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 关闭方法，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Send&lt;/code&gt; 优先级调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 几乎等同于直接调用（为什么是等同而不是直接调用？因为还需要考虑回到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 初始化时所在的线程）。&lt;/li&gt;
  &lt;li&gt;开始关闭 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvokeShutdown&lt;/code&gt;，即以指定的优先级 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 关闭方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而关闭 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 意味着所有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/BeginInvoke/InvokeAsync&lt;/code&gt; 的任务将终止。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//&amp;lt;SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  Critical - as it accesses security critical data ( window handle)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&amp;lt;/SecurityNote&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShutdownImplInSecurityContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Now that the queue is off-line, abort all pending operations,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// including inactive ones.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_instanceLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxPriority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Peek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 省略了一部分代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于此终止代码在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 所在的线程执行，而所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/BeginInvoke/InvokeAsync&lt;/code&gt; 代码也都在此线程执行，因此这些代码均不会并发。已经执行的代码会在此终止代码之前，而在此终止代码之后也不会再执行任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/BeginInvoke/InvokeAsync&lt;/code&gt; 的任务了。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;所有通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/BeginInvoke/InvokeAsync&lt;/code&gt; 或间接通过此方法（如 WPF 控件相关事件）调用的代码，均不会遭遇 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;所有在 UI 线程使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 并使用默认上下文执行的代码，均不会遭遇 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。（这意味着你没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ConfigureAwait(false)&lt;/code&gt;，详见&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;。）&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;最简示例代码&quot;&gt;最简示例代码&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-27-13-04-03.png&quot; alt=&quot;最简例子&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.ApplicationDispatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContinueWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Shutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContinueWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;结论&quot;&gt;结论&lt;/h3&gt;

&lt;p&gt;总结以上所有的分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;任何与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 不在同一个线程的代码，都可能遭遇 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;任何与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 在同一个线程的代码，都不可能遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这其实是一个线程安全问题。用所有业务开发者都可以理解的说法描述就是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;当你的应用程序退出时，所有 UI 线程的代码都不再会执行，因此这是安全的；但所有非 UI 线程的代码依然在继续执行，此时随时可能遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 属性为 null。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;因此，记得所有非 UI 线程的代码，如果需要转移到 UI 线程执行，记得判空：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnUsbDeviceChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 记得这里需要判空，因为此上下文可能在非 UI 线程。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;applicationdispatcher-实例属性&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Dispatcher&lt;/code&gt; 实例属性&lt;/h2&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Dispatcher&lt;/code&gt; 是否可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的分析，由于比较长，请参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-dispatcher-will-never-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，Dispatcher 属性一定不会为 null - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Application.cs&quot;&gt;Application.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Aug 2019 05:05:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/application-current-may-be-null.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/application-current-may-be-null.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 的 Application.Current.Dispatcher 中，Dispatcher 属性一定不会为 null</title>
        <description>&lt;p&gt;在 WPF 程序中，可能会存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current.Dispatcher.Xxx&lt;/code&gt; 这样的代码让一部分逻辑回到主 UI 线程。因为发现在调用这句代码的时候出现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;，于是就有三位小伙伴告诉我说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Current&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性都可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然而实际上这里只可能 &lt;code class=&quot;highlighter-rouge&quot;&gt;Current&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 而此上下文的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 是绝对不会为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。（当然我们这里讨论的是常规编程手段，如果非常规手段，你甚至可以让实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 呢……）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;由于本文所述的两个部分都略长，所以拆分成两篇博客，这样更容易理解。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-dispatcher-will-never-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，Dispatcher 属性一定不会为 null&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-current-may-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，为什么 Current 可能为 null&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;applicationdispatcher-实例属性&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Dispatcher&lt;/code&gt; 实例属性&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Dispatcher&lt;/code&gt; 实例属性来自于 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;源代码&quot;&gt;源代码&lt;/h3&gt;

&lt;p&gt;为了分析此属性是否可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，我现在将 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的全部代码贴在下面：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MS.Internal.WindowsBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;c1&quot;&gt;// FriendAccessAllowed&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     A DispatcherObject is an object associated with a&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;see cref=&quot;Dispatcher&quot;/&amp;gt;.  A DispatcherObject instance should&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     only be access by the dispatcher's thread.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     Subclasses of &amp;lt;see cref=&quot;DispatcherObject&quot;/&amp;gt; should enforce thread&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     safety by calling &amp;lt;see cref=&quot;VerifyAccess&quot;/&amp;gt; on all their public&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     methods to ensure the calling thread is the appropriate thread.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;para/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     DispatcherObject cannot be independently instantiated; that is,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     all constructors are protected.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherObject&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Returns the &amp;lt;see cref=&quot;Dispatcher&quot;/&amp;gt; that this&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;see cref=&quot;DispatcherObject&quot;/&amp;gt; is associated with.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// This property is free-threaded.&lt;/span&gt;
 
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;c1&quot;&gt;// This method allows certain derived classes to break the dispatcher affinity&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// of our objects.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DetachFromDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;c1&quot;&gt;// Make this object a &quot;sentinel&quot; - it can be used in equality tests, but should&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// not be used in any other way.  To enforce this and catch bugs, use a special&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// sentinel dispatcher, so that calls to CheckAccess and VerifyAccess will&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// fail;  this will catch most accidental uses of the sentinel.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureSentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureSentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// lazy creation - the first thread reaching here creates the sentinel&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// dispatcher, all other threads use it.&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinelDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Checks that the calling thread has access to this object.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Only the dispatcher thread may access DispatcherObjects.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;p/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     This method is public so that any thread can probe to&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     see if it has access to the DispatcherObject.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     True if the calling thread has access to this object.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// This method is free-threaded.&lt;/span&gt;
 
            &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
            &lt;span class=&quot;c1&quot;&gt;// Note: a DispatcherObject that is not associated with a&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// dispatcher is considered to be free-threaded.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Verifies that the calling thread has access to this object.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Only the dispatcher thread may access DispatcherObjects.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     &amp;lt;p/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     This method is public so that derived classes can probe to&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     see if the calling thread has access to itself.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// This method is free-threaded.&lt;/span&gt;
 
            &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
            &lt;span class=&quot;c1&quot;&gt;// Note: a DispatcherObject that is not associated with a&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// dispatcher is considered to be free-threaded.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     Instantiate this object associated with the current Dispatcher.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;代码来自：&lt;a href=&quot;https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherObject.cs&quot;&gt;DispatcherObject.cs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性仅仅是在获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段的值，因此我们只需要看 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段的赋值时机，以及所有给 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 赋值的代码。&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段是私有字段，所以仅需调查这个类本身即可找到所有的赋值时机。（反射等非常规手段需要排除在外，因为这意味着开发者是逗比——自己闯的祸不能怪 WPF 框架。）&lt;/p&gt;

&lt;h3 id=&quot;赋值时机&quot;&gt;赋值时机&lt;/h3&gt;

&lt;p&gt;先来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;dispatcher&lt;/code&gt; 字段的赋值时机。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 仅有一个构造函数，而这个构造函数中就已经给 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 赋值了，因此其所有的子类的初始化之前，&lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 就会被赋值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么所赋的值是否可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 呢，这就要看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.CurrentDispatcher&lt;/code&gt; 是否可能返回一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;以下是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.CurrentDispatcher&lt;/code&gt; 的属性获取代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Find the dispatcher for this thread.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Auto-create the dispatcher if there is no dispatcher for&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// this thread (if we are allowed to).&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;currentDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，无论前面的方法得到的值是否是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，后面都会再给 &lt;code class=&quot;highlighter-rouge&quot;&gt;currentDispatcher&lt;/code&gt; 局部变量赋值一个新创建的实例的。因此，此属性是绝对不会返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。&lt;/p&gt;

&lt;p&gt;由此可知，&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 自构造起便拥有一个不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性，其所有子类在初始化之前便会得到不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性。&lt;/p&gt;

&lt;h3 id=&quot;后续赋值&quot;&gt;后续赋值&lt;/h3&gt;

&lt;p&gt;现在我们来看看在初始化完成之后，后面是否有可能将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 赋值为 null。&lt;/p&gt;

&lt;p&gt;给 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段的赋值代码仅有两个：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This method allows certain derived classes to break the dispatcher affinity&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// of our objects.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DetachFromDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Make this object a &quot;sentinel&quot; - it can be used in equality tests, but should&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// not be used in any other way.  To enforce this and catch bugs, use a special&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// sentinel dispatcher, so that calls to CheckAccess and VerifyAccess will&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// fail;  this will catch most accidental uses of the sentinel.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureSentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 很好理解，让 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 无关。在整个 WPF 的代码中，使用此方法的仅有以下 6 处：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable.Freeze&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeginStoryboard.Seal&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Style.Seal&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TriggerBase.Seal&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StyleHelper&lt;/code&gt; 在 &lt;code class=&quot;highlighter-rouge&quot;&gt;SealTemplate&lt;/code&gt; 静态方法中对 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkTemplate&lt;/code&gt; 类型的实例调用此方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceDictionary&lt;/code&gt; 在构造函数中为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DummyInheritanceContext&lt;/code&gt; 属性调用此方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类型不是以上任何一个类型的子类（&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类的直接基类是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;），因此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性不可能因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 方法的调用而被赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;接下来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeSentinel&lt;/code&gt; 方法，此方法的作用不如上面方法那样直观，实际上它的作用仅仅为了验证某个方法调用时所在的线程是否是符合预期的（给 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 使用）。&lt;/p&gt;

&lt;p&gt;使用此方法的仅有 1 处：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemsControl&lt;/code&gt; 所用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemInfo&lt;/code&gt; 类的静态构造函数&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SentinelContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UnresolvedContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemovedContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItemInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// mark the special DOs as sentinels.  This helps catch bugs involving&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// using them accidentally for anything besides equality comparison.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SentinelContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UnresolvedContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;KeyContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RemovedContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所有这些使用都与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 无关。&lt;/p&gt;

&lt;h3 id=&quot;结论&quot;&gt;结论&lt;/h3&gt;

&lt;p&gt;总结以上所有的分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类型的实例在初始化之前，&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性就已经被赋值且不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;所有可能改变 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 属性的常规方法均与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类型无关；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此，所有常规手段均不会让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 值。如果你还说拿到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，那就检查是否有逗比程序员通过反射或其他手段将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段改为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 吧……&lt;/p&gt;

&lt;h2 id=&quot;applicationcurrent-静态属性&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 静态属性&lt;/h2&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt; 是否可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的分析，由于比较长，请参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/application-current-may-be-null&quot;&gt;WPF 的 Application.Current.Dispatcher 中，Current 可能为 null&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherObject.cs&quot;&gt;DispatcherObject.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Aug 2019 04:51:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/application-dispatcher-will-never-be-null.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/application-dispatcher-will-never-be-null.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 SetParent 跨进程设置父子窗口时的一些问题（小心卡死）</title>
        <description>&lt;p&gt;在微软的官方文档中，说 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 可以在进程内设置，也可以跨进程设置。当使用跨进程设置窗口的父子关系时，你需要注意本文提到的一些问题，避免踩坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;跨进程设置-setparent&quot;&gt;跨进程设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 函数设置窗口父子关系的文档可以看这个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent&quot;&gt;SetParent function (winuser.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在这篇文章的 DPI 感知一段中明确写明了在进程内以及跨进程设置父子关系时的一些行为。虽然没有明确说明支持跨进程设置父子窗口，不过这段文字就几乎说明 Windows 系统对于跨进程设置窗口父子关系还是支持的。&lt;/p&gt;

&lt;p&gt;但 Raymond Chen 在 &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/?p=4683&quot;&gt;Is it legal to have a cross-process parent/child or owner/owned window relationship?&lt;/a&gt; 一文中有另一段文字：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If I remember correctly, the documentation for &lt;code class=&quot;highlighter-rouge&quot;&gt;Set­Parent&lt;/code&gt; used to contain a stern warning that it is not supported, but that remark does not appear to be present any more. I have a customer who is reparenting windows between processes, and their application is experiencing intermittent instability.&lt;br /&gt;
如果我没记错的话，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 的文档曾经包含一个严厉的警告表明它不受支持，但现在这段备注似乎已经不存在了。我就遇到过一个客户跨进程设置窗口之间的父子关系，然后他们的应用程序间歇性不稳定。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里表明了 Raymond Chen 对于跨进程设置父子窗口的一些担忧，但从文档趋势来看，还是支持的。只是这种担忧几乎说明跨进程设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 存在一些坑。&lt;/p&gt;

&lt;p&gt;那么本文就说说跨进程设置父子窗口的一些坑。&lt;/p&gt;

&lt;h2 id=&quot;消息循环强制同步&quot;&gt;消息循环强制同步&lt;/h2&gt;

&lt;h3 id=&quot;消息循环&quot;&gt;消息循环&lt;/h3&gt;

&lt;p&gt;我们会感觉到 Windows 中某个窗口有响应（比如鼠标点击有反应），是因为这个窗口在处理 Windows 消息。窗口进行消息循环不断地处理消息使得各种各样的用户输入可以被处理，并正确地在界面上显示。&lt;/p&gt;

&lt;p&gt;一个典型的消息循环大概像这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;TranslateMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DispatchMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于显示了窗口的某个线程调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 获取了消息，Windows 系统就会认为这个线程有响应。相反，如果长时间不调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt;，Windows 就会认为这个线程无响应。&lt;code class=&quot;highlighter-rouge&quot;&gt;TranslateMessage&lt;/code&gt; 则是翻译一些消息（比如从按键消息翻译成字符消息）。真正处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 中的内容则是后面的调度消息 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchMessage&lt;/code&gt;，是这个函数的调用使得我们 UI 界面上的内容可以有可见的反映。&lt;/p&gt;

&lt;p&gt;一般来说，每个创建了窗口的线程都有自己独立的消息循环，且不会互相影响。然而一旦这些窗口之间建立了父子关系之后就会变得麻烦起来。&lt;/p&gt;

&lt;h3 id=&quot;强制同步&quot;&gt;强制同步&lt;/h3&gt;

&lt;p&gt;Windows 会让具有父子关系的所有窗口的消息循环强制同步。具体指的是，所有具有父子关系的窗口消息循环，其消息循环会串联成一个队列（这样才可以避免消息循环的并发）。&lt;/p&gt;

&lt;p&gt;也就是说，如果你有 A、B、C、D 四个窗口，分属不同进程，A 是 B、C、D 窗口的父窗口，那么当 A 在处理消息的时候，B、C、D 的消息循环就会卡在 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 的调用。同样，无论是 B、C 还是 D 在处理消息的时候，其他窗口也会同样卡在 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 的调用。这样，所有进程的 UI 线程实际上会互相等待，所有通过消息循环执行的代码都不会同时执行。然而实际上 Windows GUI 应用程序的开发中基本上 UI 代码都是通过消息循环来执行的，所以这几乎等同于所有进程的 UI 线程强制同步成类似一个 UI 线程的效果了。&lt;/p&gt;

&lt;p&gt;带来的副作用也就相当明显，任何一个进程卡了 UI，其他进程的 UI 将完全无响应。当然，不依赖消息循环的代码不会受此影响，比如 WPF 应用程序的动画和渲染。&lt;/p&gt;

&lt;h2 id=&quot;如何解决&quot;&gt;如何解决&lt;/h2&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 造成的这些问题，实际上没有官方的解决方案，你需要针对你不同的业务采用不同的解决办法。&lt;/p&gt;

&lt;p&gt;正如 Raymond Chen 所说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(It’s one of those “if you don’t already know what the consequences are, then you are not smart enough to do it correctly” things. You must first become the master of the rules before you can start breaking them.)&lt;br /&gt;
正如有些人说的“如果你不知道后果，那么你也不足以正确地完成某件事情”。在开始破坏规则之前，您必须先成为规则的主人。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;你必须清楚跨进程设置父子窗口带来的各种副作用，然后针对性地给出解决方案：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;比如所有窗口会强制串联成一个队列，那么可以考虑将暂时不显示的窗口断开父子关系；&lt;/li&gt;
  &lt;li&gt;比如设置窗口的位置大小等操作，必须考虑此窗口不是顶层窗口的问题，需要跨越进程到顶层窗口来操作；&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3459874/good-or-evil-setparent-win32-api-between-different-processes&quot;&gt;windows - Good or evil - SetParent() win32 API between different processes - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/changov/2009/10/26/hosting-wpf-ui-cross-thread-and-cross-process/&quot;&gt;Hosting WPF UI cross-thread and cross-process – Diaries of a Software Plumber&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[Is it legal to have a cross-process parent/child or owner/owned window relationship?&lt;/td&gt;
          &lt;td&gt;The Old New Thing](https://devblogs.microsoft.com/oldnewthing/?p=4683)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3152011/why-are-translatemessage-and-dispatchmessage-separate-calls&quot;&gt;winapi - Why are “TranslateMessage” and “DispatchMessage” separate calls? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Aug 2019 00:49:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/all-processes-freezes-if-their-windows-are-connected-via-setparent.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/all-processes-freezes-if-their-windows-are-connected-via-setparent.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>程序员可能会使用的各种命名规则</title>
        <description>&lt;p&gt;古老的程序员们有时会纠结命名问题，而现在，程序员们的命名已经开创了数个流派。本文整理了程序员们命名会使用到的各种流派，当然一些编程语言会同时使用数个流派。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;pascalcase&quot;&gt;PascalCase&lt;/h2&gt;

&lt;p&gt;有很多个名字，除了 PascalCase 还有 UpperCamelCase, BumpyCase。&lt;/p&gt;

&lt;p&gt;所有单词直接连接，连接的每个单词的首字母大写。&lt;/p&gt;

&lt;p&gt;WelcomeToReadWalterlvBlog&lt;/p&gt;

&lt;h2 id=&quot;camelcase&quot;&gt;camelCase&lt;/h2&gt;

&lt;p&gt;所有单词直接连接，连接的每个单词的首字母大写。&lt;/p&gt;

&lt;p&gt;walterlvIsADoubi&lt;/p&gt;

&lt;h2 id=&quot;snake_case&quot;&gt;snake_case&lt;/h2&gt;

&lt;p&gt;单词的所有字母小写，单词之间通过下划线 &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; 连接起来。&lt;/p&gt;

&lt;p&gt;welcome_to_read_walterlv_blog&lt;/p&gt;

&lt;h2 id=&quot;kebab-case&quot;&gt;kebab-case&lt;/h2&gt;

&lt;p&gt;单词的所有字母小写，单词之间通过连字符（hyphen，&lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;）连接起来。&lt;/p&gt;

&lt;p&gt;walterlv-is-a-doubi&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Letter_case#Special_case_styles&quot;&gt;Letter case - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Naming_convention_(programming)&quot;&gt;Naming convention (programming) - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 19 Aug 2019 00:44:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/programming-naming-conventions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/programming-naming-conventions.html</guid>
        
        
        <category>miscellaneous</category>
        
      </item>
    
      <item>
        <title>System.InvalidOperationException:“BuildWindowCore 无法返回寄宿的子窗口句柄。”</title>
        <description>&lt;p&gt;当试图在 WPF 窗口中嵌套显示 Win32 子窗口的时候，你有可能出现错误：“&lt;code class=&quot;highlighter-rouge&quot;&gt;BuildWindowCore 无法返回寄宿的子窗口句柄。&lt;/code&gt;”。&lt;/p&gt;

&lt;p&gt;这是很典型的 Win32 错误，本文介绍如何修复此错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 中嵌入一个其他的窗口来承载新的 WPF 控件。一般情况下我们当然不会这么去做，但是如果我们要跨越进程边界来完成 WPF 渲染内容的融合的时候，就需要嵌入一个新的窗口了。&lt;/p&gt;

&lt;p&gt;WPF 中可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt; 来包装一个 WPF 控件到 Win32 窗口，使用自定义的继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 的类可以把 Win32 窗口包装成 WPF 控件。由于窗口句柄是可以跨越进程边界传递的，所以这样的方式可以完成跨进程的 WPF 控件显示。&lt;/p&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;你有可能在调试嵌入窗口代码的时候遇到错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-14-16-08-13.png&quot; alt=&quot;错误&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.InvalidOperationException:“BuildWindowCore 无法返回寄宿的子窗口句柄。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;英文是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;BuildWindowCore failed to return the hosted child window handle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;原因和解决办法&quot;&gt;原因和解决办法&lt;/h2&gt;

&lt;p&gt;此异常的原因非常简单，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BuildWindowCore&lt;/code&gt; 的返回值有问题。具体来说，就是子窗口的句柄返回了 0。&lt;/p&gt;

&lt;p&gt;也就是下面这段代码中 &lt;code class=&quot;highlighter-rouge&quot;&gt;return new HandleRef(this, IntPtr.Zero)&lt;/code&gt; 这句，第二个参数是 0。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildWindowCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_CHILD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1073741824&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_CLIPCHILDREN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;33554432&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndSourceParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ParentWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_CHILD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_CLIPCHILDREN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TreatAncestorsAsNonClientArea&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;要解决，就需要传入正确的句柄值。当然上面的代码为了示例，故意传了一个不知道哪里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_handle&lt;/code&gt;，实际上应该传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;source.Handle&lt;/code&gt; 才是正确的。&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Aug 2019 08:32:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/failed-to-return-the-hosted-child-window-handle.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/failed-to-return-the-hosted-child-window-handle.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>System.InvalidOperationException:“寄宿 HWND 必须是子窗口。”</title>
        <description>&lt;p&gt;当试图在 WPF 窗口中嵌套显示 Win32 子窗口的时候，你有可能出现错误：“&lt;code class=&quot;highlighter-rouge&quot;&gt;System.InvalidOperationException:“寄宿 HWND 必须是子窗口。”&lt;/code&gt;”。&lt;/p&gt;

&lt;p&gt;这是很典型的 Win32 错误，本文介绍如何修复此错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个最简的嵌入其他窗口的例子&quot;&gt;一个最简的嵌入其他窗口的例子&lt;/h2&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 中嵌入一个其他的窗口来承载新的 WPF 控件。一般情况下我们当然不会这么去做，但是如果我们要跨越进程边界来完成 WPF 渲染内容的融合的时候，就需要嵌入一个新的窗口了。&lt;/p&gt;

&lt;p&gt;WPF 中可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt; 来包装一个 WPF 控件到 Win32 窗口，使用自定义的继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 的类可以把 Win32 窗口包装成 WPF 控件。由于窗口句柄是可以跨越进程边界传递的，所以这样的方式可以完成跨进程的 WPF 控件显示。&lt;/p&gt;

&lt;p&gt;下面是最简单的一个例子，为了简单，没有跨进程传递 Win32 窗口句柄，而是直接创建出来。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.HwndWrapping&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndWrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndHost&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildWindowCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndSourceParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这里的 ChildPage 是一个继承自 UseControl 的 WPF 控件，你可以自己创建自己的 WPF 控件。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChildPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DestroyWindowCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;寄宿-hwnd-必须是子窗口&quot;&gt;寄宿 HWND 必须是子窗口&lt;/h2&gt;

&lt;p&gt;当运行此代码的时候，会提示错误：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.InvalidOperationException:“寄宿 HWND 必须是子窗口。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;或者英文版：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.InvalidOperationException:”Hosted HWND must be a child window.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是一个 Win32 错误，因为我们试图将一个普通的窗口嵌入到另一个窗口中，而实际上要完成嵌入需要子窗口才行。&lt;/p&gt;

&lt;p&gt;那么如何设置一个 Win32 窗口为子窗口呢？使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowLong&lt;/code&gt; 来设置 Win32 窗口的样式是可以的。不过我们因为使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt;，所以可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSourceParameters&lt;/code&gt; 来更方便地设置窗口样式。&lt;/p&gt;

&lt;p&gt;我们需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSourceParameters&lt;/code&gt; 那一行改成这样：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  const int WS_CHILD = 0x40000000;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--  var parameters = new HwndSourceParameters(&quot;walterlv&quot;);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  var parameters = new HwndSourceParameters(&quot;walterlv&quot;)
++  {
++      ParentWindow = hwndParent.Handle,
++      WindowStyle = WS_CHILD,
++  };
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最关键的是两点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;需要设置此窗口为子窗口，也就是设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WS_CHILD&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;需要设置此窗口的父窗口，也就是设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;ParentWindow&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;hwndParent.Handle&lt;/code&gt;（我们使用参数中传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;hwndParent&lt;/code&gt; 作为父窗口）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;现在再运行，即可正常显示此嵌套窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-01-16-52-50.png&quot; alt=&quot;嵌套窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowStyle&lt;/code&gt; 属性最好加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;WS_CLIPCHILDREN&lt;/code&gt;，详情请阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/window-flickers-on-resizing-if-the-window-contains-a-hwndhost-element&quot;&gt;解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.microsoft.com/Forums/zh-CN/7090f2a0-9efc-4379-a3a7-585e209a1f54/wpf2388420837243353584329992win3224212299923124324207303403838239?forum=wpfzhchs&quot;&gt;WPF嵌入式调用Win32应用程序的问题—提示异常：寄宿的HWND必须是指定父级的子窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 14 Aug 2019 08:32:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hosted-hwnd-must-be-a-child-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hosted-hwnd-must-be-a-child-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>System.InvalidOperationException:“寄宿的 HWND 必须是指定父级的子窗口。”</title>
        <description>&lt;p&gt;当试图在 WPF 窗口中嵌套显示 Win32 子窗口的时候，你有可能出现错误：“&lt;code class=&quot;highlighter-rouge&quot;&gt;寄宿的 HWND 必须是指定父级的子窗口。&lt;/code&gt;”。&lt;/p&gt;

&lt;p&gt;这是很典型的 Win32 错误，本文介绍如何修复此错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 中嵌入一个其他的窗口来承载新的 WPF 控件。一般情况下我们当然不会这么去做，但是如果我们要跨越进程边界来完成 WPF 渲染内容的融合的时候，就需要嵌入一个新的窗口了。&lt;/p&gt;

&lt;p&gt;WPF 中可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt; 来包装一个 WPF 控件到 Win32 窗口，使用自定义的继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 的类可以把 Win32 窗口包装成 WPF 控件。由于窗口句柄是可以跨越进程边界传递的，所以这样的方式可以完成跨进程的 WPF 控件显示。&lt;/p&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;你有可能在调试嵌入窗口代码的时候遇到错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-14-16-17-50.png&quot; alt=&quot;错误&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.InvalidOperationException:“寄宿的 HWND 必须是指定父级的子窗口。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;英文是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hosted HWND must be a child window of the specified parent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;原因和解决办法&quot;&gt;原因和解决办法&lt;/h2&gt;

&lt;p&gt;出现此错误，是因为同一个子窗口被两次设置为同一个窗口的子窗口。&lt;/p&gt;

&lt;p&gt;具体来说，就是 A 窗口使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 设置成了 B 的子窗口，随后 A 又通过一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 设置成了新子窗口。&lt;/p&gt;

&lt;p&gt;要解决，则必须确保一个窗口只能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndHost&lt;/code&gt; 设置一次子窗口。&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Aug 2019 08:32:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hosted-hwnd-must-be-a-child-window-of-the-specified-parent.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hosted-hwnd-must-be-a-child-window-of-the-specified-parent.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包</title>
        <description>&lt;p&gt;MSBuild 的 Task 为我们扩展项目的编译过程提供了强大的扩展性，它使得我们可以用 C# 语言编写扩展；利用这种扩展性，我们可以为我们的项目定制一部分的编译细节。NuGet 为我们提供了一种自动导入 .props 和 .targets 的方法，同时还是一个 .NET 的包平台；我们可以利用 NuGet 发布我们的工具并自动启用这样的工具。&lt;/p&gt;

&lt;p&gt;制作这样的一个跨平台 NuGet 工具，我们能够为安装此工具的项目提供自动的但定制化的编译细节——例如自动生成版本号，自动生成某些中间文件等。&lt;/p&gt;

&lt;p&gt;本文更偏向于入门，只在帮助你一步一步地制作一个最简单的 NuGet 工具包，以体验和学习这个过程。然后我会在另一篇博客中完善其功能，做一个完整可用的 NuGet 工具。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;关于创建跨平台 NuGet 工具包的博客，我写了两篇。一篇介绍写基于 MSBuild Task 的 dll，一篇介绍写任意的命令行工具，可以是用于 .NET Framework 的 exe，也可以是基于 .NET Core 的 dll，甚至可以是使用本机工具链编译的平台相关的各种格式的命令行工具。内容是相似的但关键的坑不同。我分为两篇可以减少完成单个任务的理解难度：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第零步前置条件&quot;&gt;第零步：前置条件&lt;/h2&gt;

&lt;h2 id=&quot;第一步创建一个项目用来写工具的核心逻辑&quot;&gt;第一步：创建一个项目，用来写工具的核心逻辑&lt;/h2&gt;

&lt;p&gt;为了方便制作跨平台的 NuGet 工具，新建项目时我们优先选用 .NET Core Library 项目或 .NET Standard Library 项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-19-26-03.png&quot; alt=&quot;新建一个项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;紧接着，我们需要打开编辑此项目的 .csproj 文件，将目标框架改成多框架的，并填写必要的信息。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 给一个初始的版本号。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 使用 .NET Framework 4.7 和 .NET Core 2.0。
      要点 1：
        - 加入 net47 的支持是为了能让基于 .NET Framework 的 msbuild 能够使用此工具编译；
        - 加入 netcoreapp2.0 的支持是为了能让基于 .NET Core 的 dotnet build (Roslyn) 能够使用此工具编译；
        - 当然 net47 太新了，只适用于 Visual Studio 2017 的较新版本，如果你需要照顾到更多用户，建议使用 net46。
      要点 2：
        注意，我们使用 NuGet 包来依赖 Task 框架，但此 NuGet 包要求的最低 .NET Framework 版本为 4.6。
        如果需要制作 .NET Framework 4.5 及以下版本，就必须改为引用以下程序集：
        - Microsoft.Build
        - Microsoft.Build.Framework
        - Microsoft.Build.Tasks.v4.0
        - Microsoft.Build.Utilities.v4.0 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net47;netcoreapp2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个就是创建项目时使用的名称。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.NuGetTool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此值设为 true，才会在编译之后生成 NuGet 包。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 作者的 Id，如果要发布到 nuget.org，那么这里就是 NuGet 用户 Id。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，安装如下 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft.Build.Framework: &lt;em&gt;提供了编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;ITask&lt;/code&gt; 的框架，有了这个才能写 &lt;code class=&quot;highlighter-rouge&quot;&gt;ITask&lt;/code&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft.Build.Utilities.Core: &lt;em&gt;提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ITask&lt;/code&gt; 框架的基本实现，这样才能用更少的代码写完 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-19-31-51.png&quot; alt=&quot;安装 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;要特别注意：由于我们是一个 NuGet 工具，不需要被其他项目直接依赖，所以此项目的依赖包不应该传递到下一个项目中。所以&lt;strong&gt;请将所有的 NuGet 包资产都声明成私有的&lt;/strong&gt;，方法是在 NuGet 包的引用后面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrivateAssets=&quot;All&quot;&lt;/code&gt;。想了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrivateAssets&lt;/code&gt; 的含义一起相关属性，可以阅读我的另一篇文章&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Framework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.6.85&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Utilities.Core&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.6.85&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(PackageReference)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来就是取名字的时间了！为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Class1&lt;/code&gt; 类改一个名字。这个类将成为我们这个 NuGet 工具包的入口类。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;比如我们想做一个用 Git 提交信息来生成版本号的类，可以叫做 GitVersion；想做一个生成多语言文件的类，可以叫做 LangGenerator。在这里，为了示范而不是真正的实现功能，我取名为 DemoTool。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;取好名字之后，让这个类继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Build.Utilities.Task&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// DemoTool.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时进行编译，我们的 NuGet 包就会出现在项目的输出目录 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 下了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-20-04-21.png&quot; alt=&quot;输出目录下的 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步组织-nuget-目录&quot;&gt;第二步：组织 NuGet 目录&lt;/h2&gt;

&lt;p&gt;刚刚生成的 NuGet 包还不能真正拿来用。事实上你也可以拿去安装，不过最终的效果只是加了一个毫无作用的引用程序集而已（顺便还带来一堆垃圾的间接引用）。&lt;/p&gt;

&lt;p&gt;所以，我们需要进行“一番配置”，使得这个项目编译成一个&lt;strong&gt;NuGet 工具&lt;/strong&gt;，而不是一个&lt;strong&gt;依赖包&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;现在，介绍一下 NuGet 预设的目录（如果你想看，可以去解压 .nupkg 文件）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 根目录，用来放 readme.txt 的（已经有人提 issue 要求加入 markdown 支持了）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 用来放引用程序集 .dll，文档注释 .xml 和符号文件 .pdb 的&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 用来放那些与平台相关的 .dll/.pdb/.pri 的&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 任意种类的文件，在这个文件夹中的文件会在编译时拷贝到输出目录（保持文件夹结构）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里放 .props 和 .targets 文件，会自动被 NuGet 导入，成为项目的一部分（要求文件名与包名相同）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里也是放 .props 和 .targets 文件，会自动被 NuGet 导入，成为项目的一部分（要求文件名与包名相同）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildMultiTargeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// PowerShell 脚本或者程序，在这里的工具可以在“包管理控制台”(Package Manager Console) 中使用&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 以上结构可以去官网翻阅原文 &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package?wt.mc_id=MVP&quot;&gt;How to create a NuGet package - Microsoft Docs&lt;/a&gt;，不过我这里额外写了一个预设目录 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt;，官方文档却没有说。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意到我们的 csproj 文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/code&gt; 节点吗？如果指定为单个框架，则自动导入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 目录下的；如果指定为多个框架，则自动导入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 目录下的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我们的初衷是做一个 NuGet 工具，所以我们需要选择合适的目录来存放我们的输出文件。&lt;/p&gt;

&lt;p&gt;我们要放一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.targets&lt;/code&gt; 文件到 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹中，以便能够让我们定制编译流程。我们要让我们写的 dll（也就是那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;）能够工作，但是以上任何预定义的文件夹都不能满足我们的要求，于是我们建一个自定义的文件夹，取名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt;，这样 NuGet 便不会对我们的这个 dll 进行特殊处理，而将处理权全部交给我们。&lt;/p&gt;

&lt;p&gt;于是我们自己的目录结构为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targets&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildMultiTargeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targets&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;net47&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dll&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcoreapp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dll&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，如何改造我们的项目才能够生成这样的 NuGet 目录结构呢？&lt;/p&gt;

&lt;p&gt;我们先在 Visual Studio 里建好文件夹：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-20-40-51.png&quot; alt=&quot;Visual Studio 里的目录结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后去编辑项目的 .csproj 文件，在最后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/code&gt; 前面添加下面这些项：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\readme.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 表示这一项要显示到 Visual Studio 解决方案中（其实对于不认识的文件，&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 就是默认值）；&lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt; 表示相对于项目文件的路径（支持通配符）；&lt;code class=&quot;highlighter-rouge&quot;&gt;Pack&lt;/code&gt; 表示这一项要打包到 NuGet；&lt;code class=&quot;highlighter-rouge&quot;&gt;PackagePath&lt;/code&gt; 表示这一项打包到 NuGet 中的路径。(&lt;em&gt;如果你想了解更多 csproj 中的 NuGet 属性，可以阅读我的另一篇文章：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;这样的一番设置，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;readme.txt&lt;/code&gt; 准备好了，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt; 文件夹还没有。由于我们是把我们生成的 dll 放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt; 里面，第一个想到的当然是修改输出路径——然而这是不靠谱的，因为 NuGet 并不识别输出路径。事实上，我们还可以设置一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/code&gt;，将值指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt;，那么我们就能够将我们的输出文件打包到 NuGet 对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt; 文件夹下了。&lt;/p&gt;

&lt;p&gt;至此，我们的 .csproj 文件看起来像如下这样（为了减少行数，我已经去掉了注释）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.NuGetTool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/span&gt;tasks&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildOutputTargetFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoPackageAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoPackageAnalysis&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Framework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.6.85&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Utilities.Core&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.6.85&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 在第一步中不要忘了这一行 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(PackageReference)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Folder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\tasks\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的三项 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\readme.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到我同时还在文件中新增了另外两个属性配置 &lt;code class=&quot;highlighter-rouge&quot;&gt;NoPackageAnalysis&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DevelopmentDependency&lt;/code&gt;。由于我们没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;lib&lt;/code&gt; 文件夹，所以 NuGet 会给出警告，&lt;code class=&quot;highlighter-rouge&quot;&gt;NoPackageAnalysis&lt;/code&gt; 将阻止这个警告。&lt;code class=&quot;highlighter-rouge&quot;&gt;DevelopmentDependency&lt;/code&gt; 是为了说明这是一个开发依赖，设置为 true 将阻止包作为依赖传递给下一个项目。（&lt;strong&gt;事实上这又是官方的一个骗局！因为新版本的 NuGet 竟然去掉了这个功能！&lt;/strong&gt;，已经被吐槽了，详见：&lt;a href=&quot;https://github.com/NuGet/Home/issues/4125&quot;&gt;PackageReference should support DevelopmentDependency metadata · Issue #4125 · NuGet/Home&lt;/a&gt;）。关于这些属性更详细的解释，依然可以参见：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;现在再尝试编译一下我们的项目，去输出目录下解压查看 nupkg 文件，你就能看到期望的 NuGet 文件夹结构了；建议一个个点进去看，你可以看到我们准备好的空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.targets&lt;/code&gt; 文件，也能看到我们生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.dll&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-20-54-45.png&quot; alt=&quot;生成的 NuGet 包的目录结构&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步编写-target&quot;&gt;第三步：编写 Target&lt;/h2&gt;

&lt;p&gt;.targets 文件是对项目功能进行扩展的关键文件，由于安装 NuGet 包会自动导入包中的此文件，所以它几乎相当于我们功能的入口。&lt;/p&gt;

&lt;p&gt;现在，我们需要徒手编写这个文件了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 我们使用 $(MSBuildRuntimeType) 来判断编译器是 .NET Core 的还是 .NET Framework 的。
         然后选用对应的文件夹。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' == 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tasks\netcoreapp2.0\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' != 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tasks\net47\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;UsingTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NuGetTool.DemoTool&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AssemblyFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetWalterlvTaskFolder)\Walterlv.NuGetTool.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoTool&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;targets 的文件结构与 csproj 是一样的，你可以阅读我的另一篇文章 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt; 了解其结构。&lt;/p&gt;

&lt;p&gt;上面的文件中，我们指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的执行时机为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 之前，也就是编译那些 .cs 文件之前。在这个时机，我们可以修改要编译的 .cs 文件。如果想了解更多关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 执行时机或顺序相关的资料，可以阅读：&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ee216359.aspx&quot;&gt;Target Build Order&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;别忘了我们还有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹，也要放一个几乎一样功能的 targets 文件；不过我们肯定不会傻到复制一个一样的。我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹里的 targets 文件中写以下内容，这样我们的注意力便可以集中在前面的 targets 文件中了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\buildMultiTargeting\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 直接 Import 我们在 build 中写的那个 targets 文件。
       NuGet 留下了为多框架项目提供特殊扩展的方案，其实有时候也是很有用的。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\build\Walterlv.NuGetTool.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第四部调试&quot;&gt;第四部：调试&lt;/h2&gt;

&lt;p&gt;严格来说，写到这里，我们的跨平台 NuGet 工具已经写完了。在以上状态下，你只需要编译一下，就可以获得一个跨平台的基于 MSBuild Task 的 NuGet 工具。只是——你肯定会非常郁闷——心里非常没谱，这工具到底有没有工作起来！有没有按照我预期的进行工作！如果遇到了 Bug 怎么办！&lt;/p&gt;

&lt;p&gt;于是现在我们来掌握一些调试技巧，这样才方便我们一步步完善我们的功能嘛！&lt;strong&gt;额外插一句：以上第一到第三步几乎都是结构化的步骤，其实非常适合用工具来自动化完成的。&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;让我们的-target-能够正确找到我们新生成的-dll&quot;&gt;让我们的 Target 能够正确找到我们新生成的 dll&lt;/h3&gt;

&lt;p&gt;你应该注意到，我们的 targets 文件在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\build&lt;/code&gt; 目录下，而我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 文件夹下并没有真实的 &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt; 文件夹（里面是空的）。于是我们希望在调试状态下，dll 能够指向输出目录下。于是我们修改 targets 文件添加配置：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; $(IsInDemoToolDebugMode) == 'True' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' == 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\..\bin\$(Configuration)\netcoreapp2.0\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' != 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\..\bin\$(Configuration)\net47\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; $(IsInDemoToolDebugMode) != 'True' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' == 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tasks\netcoreapp2.0\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvTaskFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(MSBuildRuntimeType)' != 'Core'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tasks\net47\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvTaskFolder&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;UsingTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NuGetTool.DemoTool&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AssemblyFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetWalterlvTaskFolder)\Walterlv.NuGetTool.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoTool&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们就拥有了一个可以供用户设置的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/code&gt; 了。&lt;/p&gt;

&lt;h3 id=&quot;准备一个用于测试-task-的测试项目&quot;&gt;准备一个用于测试 Task 的测试项目&lt;/h3&gt;

&lt;p&gt;接着，我们在解决方案中新建一个调试项目 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Debug&lt;/code&gt;（我选用了 .NET Standard 2.0 框架）。然后在它的 csproj 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Import&amp;gt;&lt;/code&gt; 我们刚刚的 .targets 文件，并设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.Debug.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsInDemoToolDebugMode&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.NuGetTool\Assets\build\Walterlv.NuGetTool.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当准备好基本的调试环境之后，我们的解决方案看起来是下面这样的样子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-02-57.png&quot; alt=&quot;带有调试环境的解决方案&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;让我们自定义的-task-开始工作并能够进入断点&quot;&gt;让我们自定义的 Task 开始工作，并能够进入断点&lt;/h3&gt;

&lt;p&gt;最简单能够让 DemoTool 这个自定义的 Task 进入断点的方式当然是加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch();&lt;/code&gt; 了，就像这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// DemoTool.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 新增了启动调试器的代码。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，一旦此函数开始执行，Windows 将显示一个选择调试器的窗口，我们选择当前打开的 Visual Studio 即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-07-18.png&quot; alt=&quot;选择调试器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，也有一些比较正统的方法，为了使这篇文章尽可能简单，我只附一张图，如果有需要，可以自己去尝试：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-11-59.png&quot; alt=&quot;使用“调试配置”调试&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们去 Walterlv.Debug 目录下输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令，在输出到如下部分的时候，就会进入我们的断点了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-15-56.png&quot; alt=&quot;进入了断点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这下，我们的调试环境就全部搭建好了，你可以发挥你的想象力在 Task 里面随意挥洒你的代码！&lt;/p&gt;

&lt;p&gt;当然，只要你记得去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch();&lt;/code&gt;，或者加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 这样的条件编译，那么随时打包就是一个可以发布的跨平台 NuGet 工具包了。&lt;/p&gt;

&lt;p&gt;提示：&lt;strong&gt;一旦调试环境搭建好，你可能会遇到编译 Walterlv.NuGetTool 项目时，发现 dll 被占用的情况，这时，打开任务管理器结束掉 msbuild.exe 进行即可。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;第五步发挥你的想象力&quot;&gt;第五步：发挥你的想象力&lt;/h2&gt;

&lt;p&gt;想象力是没有限制的，不过如果不知道 Task 能够为我们提供到底什么样的功能，也是无从下手的。这一节我会说一些 Task 在 C# 代码和 .targets 文件中的互相操作。&lt;/p&gt;

&lt;h3 id=&quot;targets-向-task-传参数&quot;&gt;.targets 向 Task 传参数&lt;/h3&gt;

&lt;p&gt;.targets 向 Task 传参数只需要写一个属性赋值的句子就可以了：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoTool&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IntermediateOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)&lt;/code&gt; 是 msbuild 编译期间会自动设置的全局属性，代表此项目编译过程中临时文件的存放路径（也就是我们常见的 obj 文件夹）。当然，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet msbuild&lt;/code&gt; 也是有这样的全局属性的。我们为 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;DemoTool&amp;gt;&lt;/code&gt; 节点也加了一个属性，名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;IntermediateOutputPath&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在 DemoTool 的 C# 代码中，只需要写一个字符串属性即可接收这样的传参。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// DemoTool.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntermediateOutputPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediateOutputPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntermediateOutputPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-45-50.png&quot; alt=&quot;在 DemoTool 中调试查看传进来的参数&quot; /&gt;&lt;br /&gt;
▲ 在断点中我们能够看到传进来的参数的值&lt;/p&gt;

&lt;p&gt;你可以尽情发挥你的想象力，传入更多让人意想不到的参数，实现不可思议的功能。更多 MSBuild 全局参数，可以参考我的另一篇文章&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;task-向-targets-返回参数&quot;&gt;Task 向 .targets 返回参数&lt;/h3&gt;

&lt;p&gt;如果只是传入参数，那么我们顶多只能干一些不痛不痒的事情，或者就是两者互相约定了一些常量。什么？你说直接去改源代码？那万一你的代码不幸崩溃了，项目岂不被你破坏了！（当然，你去改了源码，还会破坏 MSBuild 的差量编译。）&lt;/p&gt;

&lt;p&gt;我们新定义一个属性，但在属性上面标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Output]&lt;/code&gt; 特性。这样，这个属性就会作为输出参数传到 .targets 里了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// DemoTool.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Framework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntermediateOutputPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdditionalCompileFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediateOutputPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntermediateOutputPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;additional&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intermediateOutputPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DoubiClass.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AdditionalCompileFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;additional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AdditionalCompileFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;@&quot;using System;
namespace Walterlv.Debug
{
    public class Doubi
    {
        public string Name { get; }
        private Doubi(string name) =&amp;gt; Name = name;
        public static Doubi Get() =&amp;gt; new Doubi(&quot;&quot;吕毅&quot;&quot;);
    }
}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，我们在 .targets 里接收这个输出参数，生成一个属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoTool&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IntermediateOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdditionalCompileFile&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo_AdditionalCompileFile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DemoTool&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemo_AdditionalCompileFile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们生成的 Walterlv.Debug 调试项目在编译完成之后，还会额外多出一个“逗比”类。而且——我们甚至能够直接在 Walterlv.Debug 项目的中使用这个编译中生成的新类。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-23-37-47.png&quot; alt=&quot;可以使用额外生成的类&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用编译生成的新类既不会报错，也不会产生警告下划线，就像原生写的类一样。&lt;/p&gt;

&lt;p&gt;如果你要在编译期间替换一个类而不是新增一个类，例如将 Class1.cs 更换成新类，那么需要将其从编译列表中移除：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Class1.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemo_AdditionalCompileFile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意：&lt;strong&gt;编译期间才生成的项（&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt;）或者属性（&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt;），需要写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 节点的里面。&lt;/strong&gt;如果写在外面，则不是编译期间生效的，而是始终生效的。当写在外面时，要特别留意可能某些属性没有初始化完全，你应该只使用那些肯定能确认存在的属性或文件。&lt;/p&gt;

&lt;h3 id=&quot;在-target-里编写调试代码&quot;&gt;在 Target 里编写调试代码&lt;/h3&gt;

&lt;p&gt;虽然说以上的每一个步骤我都是一边实操一边写的，但即便如此，本文都写了 500 多行了，如果你依然能够不出错地完成以上每一步，那也是万幸了！Task 里我能还能用断点调试，那么 Target 里面怎么办呢？&lt;/p&gt;

&lt;p&gt;我们可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Message&amp;gt;&lt;/code&gt; 节点来输出一些信息：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoTool&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IntermediateOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdditionalCompileFile&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo_AdditionalCompileFile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DemoTool&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;临时文件的路径为：$(WalterlvDemo_AdditionalCompileFile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemo_AdditionalCompileFile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-23-50-41.png&quot; alt=&quot;输出信息&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在-task-输出错误或警告&quot;&gt;在 Task 输出错误或警告&lt;/h3&gt;

&lt;p&gt;我们继承了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Build.Utilities.Task&lt;/code&gt;，此类有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Log&lt;/code&gt; 属性，可以用来输出信息。使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;LogWarning&lt;/code&gt; 方法可以输出警告，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;LogError&lt;/code&gt; 可以输出错误。如果输出了错误，那么就会导致编译不通过。&lt;/p&gt;

&lt;h3 id=&quot;加入差量编译支持&quot;&gt;加入差量编译支持&lt;/h3&gt;

&lt;p&gt;如果你觉得你自己写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 执行非常耗时，那么建议加入差量编译的支持。关于加入差量编译，可以参考我的另一篇文章&lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;本地测试-nuget-包&quot;&gt;本地测试 NuGet 包&lt;/h3&gt;

&lt;p&gt;在发布 NuGet 包之前，我们可以先在本地安装测试。由于我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\Desktop\Walterlv.NuGetTool\Walterlv.NuGetTool\bin\Debug&lt;/code&gt; 输出路径下已经有了打包好的 nupkg 文件，所以可以加一个本地 NuGet 源。&lt;/p&gt;

&lt;p&gt;我们找一个其他的项目，然后在 Visual Studio 中设置 NuGet 源为我们那个 NuGet 工具项目的输出路径。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-23-53-47.png&quot; alt=&quot;设置本地 NuGet 包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时安装，编译完之后，我们就会发现我们的项目生成的 dll 中多出了一个“逗比(Doubi)”类，并且可以在那个项目中编写使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Doubi&lt;/code&gt; 的代码了。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;不得不说，制作一个跨平台的基于 MSBuild Task 的 NuGet 工具包还是比较麻烦的，我们总结一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;准备项目的基本配置（设置各种必要的项目属性，安装必要的 NuGet 依赖）&lt;/li&gt;
  &lt;li&gt;建立好 NuGet 的文件夹结构&lt;/li&gt;
  &lt;li&gt;编写 Task 和 Target&lt;/li&gt;
  &lt;li&gt;新增功能、调试和测试&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你在实践的过程中遇到了各种问题，欢迎在下面留言，一般我会在一天之内给予回复。&lt;/p&gt;

&lt;p&gt;如果在阅读这篇文章时存在一些概念理解上的问题，或者不知道如何扩展本文的功能，可能需要阅读下我的另一些文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，还有一些正在编写，过一段时间可以阅读到。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/docs.microsoft.com-nuget/blob/master/docs/create-packages/Creating-a-Package.md&quot;&gt;docs.microsoft.com-nuget/Creating-a-Package.md at master · NuGet/docs.microsoft.com-nuget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets?wt.mc_id=MVP&quot;&gt;NuGet pack and restore as MSBuild targets - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.natemcmaster.com/blog/2017/11/11/build-tools-in-nuget/&quot;&gt;Bundling .NET build tools in NuGet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.natemcmaster.com/blog/2017/07/05/msbuild-task-in-nuget/&quot;&gt;Shipping a cross-platform MSBuild task in a NuGet package&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms164309.aspx&quot;&gt;MSBuild Reserved and Well-Known Properties&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/6982575/6233938&quot;&gt;build process - How does MSBuild check whether a target is up to date or not? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms171483.aspx&quot;&gt;How to: Build Incrementally&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/msbuild/2006/01/21/how-to-implementing-custom-tasks-part-i/&quot;&gt;How To: Implementing Custom Tasks – Part I – MSBuild Team Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/1367309/6233938&quot;&gt;Overwrite properties with MSBuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/4ba7e9a0-76e6-4b1c-8536-fd76a5b96c79/how-to-access-msbuild-properties-inside-custom-task?forum=vsx&quot;&gt;How to Access MSBuild properties inside custom task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/39745383/6233938&quot;&gt;visual studio - How to get property value of a project file using msbuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/davidfowl/NuGetPowerTools&quot;&gt;davidfowl/NuGetPowerTools: A bunch of powershell modules that make it even easier to work with nuget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/27377095/6233938&quot;&gt;MSBuild and Skipping target &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&amp;lt;TargetName&amp;gt;&quot;&lt;/code&gt; because it has no outputs - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ff598685.aspx&quot;&gt;WriteCodeFragment Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/15012963/6233938&quot;&gt;Don’t include dependencies from packages.config file when creating NuGet package - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/nuget/release-notes/nuget-2.7#Development-Only_Dependencies?wt.mc_id=MVP&quot;&gt;NuGet 2.7 Release Notes - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/Home/issues/4125&quot;&gt;PackageReference should support DevelopmentDependency metadata · Issue #4125 · NuGet/Home&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/357445/6233938&quot;&gt;debugging - How to debug MSBuild Customtask - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 07 Aug 2019 06:50:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-a-cross-platform-msbuild-task-based-nuget-tool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-a-cross-platform-msbuild-task-based-nuget-tool.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>如何创建一个基于命令行工具的跨平台的 NuGet 工具包</title>
        <description>&lt;p&gt;命令行可是跨进程通信的一种非常方便的手段呢，只需启动一个进程传入一些参数即可完成一些很复杂的任务。NuGet 为我们提供了一种自动导入 .props 和 .targets 的方法，同时还是一个 .NET 的包平台；我们可以利用 NuGet 发布我们的工具并自动启用这样的工具。&lt;/p&gt;

&lt;p&gt;制作这样的一个跨平台 NuGet 工具，我们能够为安装此工具的项目提供自动的但定制化的编译细节——例如自动生成版本号，自动生成某些中间文件等。&lt;/p&gt;

&lt;p&gt;本文更偏向于入门，只在帮助你一步一步地制作一个最简单的 NuGet 工具包，以体验和学习这个过程。然后我会在另一篇博客中完善其功能，做一个完整可用的 NuGet 工具。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;关于创建跨平台 NuGet 工具包的博客，我写了两篇。一篇介绍写基于 MSBuild Task 的 dll，一篇介绍写任意的命令行工具，可以是用于 .NET Framework 的 exe，也可以是基于 .NET Core 的 dll，甚至可以是使用本机工具链编译的平台相关的各种格式的命令行工具。内容是相似的但关键的坑不同。我分为两篇可以减少完成单个任务的理解难度：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第零步前置条件&quot;&gt;第零步：前置条件&lt;/h2&gt;

&lt;h2 id=&quot;第一步创建一个项目用来写工具的核心逻辑&quot;&gt;第一步：创建一个项目，用来写工具的核心逻辑&lt;/h2&gt;

&lt;p&gt;为了方便制作跨平台的 NuGet 工具，新建项目时我们优先选用 .NET Core 控制台项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-07-46-48.png&quot; alt=&quot;新建一个项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;紧接着，我们需要打开编辑此项目的 .csproj 文件，填写必要的信息（尤其是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/code&gt;，确保编译时会生成 NuGet 包）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 输出为 exe（其实对于 .NET Core 依然是 dll，除非进行发布）。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 给一个初始的版本号。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 由于 .NET Core 本身即具备跨平台的特性，所以我们直接基于 .NET Core 开发 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个就是创建项目时使用的名称。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.NuGetTool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此值设为 true，才会在编译之后生成 NuGet 包。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 作者的 Id，如果要发布到 nuget.org，那么这里就是 NuGet 用户 Id。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来随便在 Program.cs 里写什么代码，这取决于你希望这个 NuGet 工具做什么。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时进行编译，我们的 NuGet 包就会出现在项目的输出目录 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 下了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-07-52-23.png&quot; alt=&quot;输出目录下的 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步组织-nuget-目录&quot;&gt;第二步：组织 NuGet 目录&lt;/h2&gt;

&lt;p&gt;刚刚生成的 NuGet 包还不能真正拿来用。事实上你也可以拿去安装，不过最终的效果只是加了一个毫无作用的引用程序集而已（事实上就是把你写的程序作为普通 dll 引用了）。&lt;/p&gt;

&lt;p&gt;所以，我们需要进行“一番配置”，使得这个项目编译成一个&lt;strong&gt;NuGet 工具&lt;/strong&gt;，而不是一个&lt;strong&gt;依赖包&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;现在，介绍一下 NuGet 预设的目录（如果你想看，可以去解压 .nupkg 文件）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 根目录，用来放 readme.txt 的（已经有人提 issue 要求加入 markdown 支持了）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 用来放引用程序集 .dll，文档注释 .xml 和符号文件 .pdb 的&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 用来放那些与平台相关的 .dll/.pdb/.pri 的&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 任意种类的文件，在这个文件夹中的文件会在编译时拷贝到输出目录（保持文件夹结构）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里放 .props 和 .targets 文件，会自动被 NuGet 导入，成为项目的一部分（要求文件名与包名相同）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里也是放 .props 和 .targets 文件，会自动被 NuGet 导入，成为项目的一部分（要求文件名与包名相同）&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildMultiTargeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// PowerShell 脚本或者程序，在这里的工具可以在“包管理控制台”(Package Manager Console) 中使用&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 以上结构可以去官网翻阅原文 &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package?wt.mc_id=MVP&quot;&gt;How to create a NuGet package - Microsoft Docs&lt;/a&gt;，不过我这里额外写了一个预设目录 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt;，官方文档却没有说。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意到我们的 csproj 文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/code&gt; 节点吗？如果指定为单个框架，则自动导入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 目录下的；如果指定为多个框架，则自动导入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 目录下的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我们的初衷是做一个 NuGet 工具，所以我们需要选择合适的目录来存放我们的输出文件。&lt;/p&gt;

&lt;p&gt;我们要放一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.targets&lt;/code&gt; 文件到 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹中，以便能够让我们定制编译流程。我们要让我们写的 .NET Core 工具程序能够工作，所以我们将生成的输出程序放到 tools 目录下。&lt;/p&gt;

&lt;p&gt;于是我们自己的目录结构为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targets&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildMultiTargeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targets&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcoreapp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dll&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;提醒一下，.NET Core 生成的程序，如果没有针对特定平台发布，输出的是 dll。&lt;/p&gt;

&lt;p&gt;那么，如何改造我们的项目才能够生成这样的 NuGet 目录结构呢？&lt;/p&gt;

&lt;p&gt;我们先在 Visual Studio 里建好文件夹：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-07-57-30.png&quot; alt=&quot;Visual Studio 里的目录结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后去编辑项目的 .csproj 文件，在最后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/code&gt; 前面添加下面这些项：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\readme.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 表示这一项要显示到 Visual Studio 解决方案中（其实对于不认识的文件，&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 就是默认值）；&lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt; 表示相对于项目文件的路径（支持通配符）；&lt;code class=&quot;highlighter-rouge&quot;&gt;Pack&lt;/code&gt; 表示这一项要打包到 NuGet；&lt;code class=&quot;highlighter-rouge&quot;&gt;PackagePath&lt;/code&gt; 表示这一项打包到 NuGet 中的路径。(&lt;em&gt;如果你想了解更多 csproj 中的 NuGet 属性，可以阅读我的另一篇文章：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;这样的一番设置，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;readme.txt&lt;/code&gt; 准备好了，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt; 文件夹还没有。由于我们是把我们生成的命令行工具放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt; 里面，第一个想到的当然是修改输出路径——然而这是不靠谱的，因为 NuGet 并不识别输出路径。事实上，我们还可以设置一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/code&gt;，将值指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt;，那么我们就能够将我们的输出文件打包到 NuGet 对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt; 文件夹下了。&lt;/p&gt;

&lt;p&gt;至此，我们的 .csproj 文件看起来像如下这样（为了减少行数，我已经去掉了注释）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetTool.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.NuGetTool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/span&gt;tools&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildOutputTargetFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoPackageAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoPackageAnalysis&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Folder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\tools\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ↓ 新增的三项 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\buildMultiTargeting\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;buildMultiTargeting\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\readme.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到我同时还在文件中新增了另外两个属性配置 &lt;code class=&quot;highlighter-rouge&quot;&gt;NoPackageAnalysis&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DevelopmentDependency&lt;/code&gt;。由于我们没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;lib&lt;/code&gt; 文件夹，所以 NuGet 会给出警告，&lt;code class=&quot;highlighter-rouge&quot;&gt;NoPackageAnalysis&lt;/code&gt; 将阻止这个警告。&lt;code class=&quot;highlighter-rouge&quot;&gt;DevelopmentDependency&lt;/code&gt; 是为了说明这是一个开发依赖，设置为 true 将阻止包作为依赖传递给下一个项目。（&lt;strong&gt;事实上这又是官方的一个骗局！因为新版本的 NuGet 竟然去掉了这个功能！&lt;/strong&gt;，已经被吐槽了，详见：&lt;a href=&quot;https://github.com/NuGet/Home/issues/4125&quot;&gt;PackageReference should support DevelopmentDependency metadata · Issue #4125 · NuGet/Home&lt;/a&gt;）。关于这些属性更详细的解释，依然可以参见：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;现在再尝试编译一下我们的项目，去输出目录下解压查看 nupkg 文件，你就能看到期望的 NuGet 文件夹结构了；建议一个个点进去看，你可以看到我们准备好的空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.targets&lt;/code&gt; 文件，也能看到我们生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGetTool.dll&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-08-03-01.png&quot; alt=&quot;生成的 NuGet 包的目录结构&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步编写-target&quot;&gt;第三步：编写 Target&lt;/h2&gt;

&lt;p&gt;.targets 文件是对项目功能进行扩展的关键文件，由于安装 NuGet 包会自动导入包中的此文件，所以它几乎相当于我们功能的入口。&lt;/p&gt;

&lt;p&gt;现在，我们需要徒手编写这个文件了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tools\netcoreapp2.0\Walterlv.NuGetTool.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet $(NuGetWalterlvToolPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;targets 的文件结构与 csproj 是一样的，你可以阅读我的另一篇文章 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt; 了解其结构。&lt;/p&gt;

&lt;p&gt;上面的文件中，我们指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的执行时机为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 之前，也就是编译那些 .cs 文件之前。在这个时机，我们可以修改要编译的 .cs 文件。如果想了解更多关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 执行时机或顺序相关的资料，可以阅读：&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ee216359.aspx&quot;&gt;Target Build Order&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;别忘了我们还有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹，也要放一个几乎一样功能的 targets 文件；不过我们肯定不会傻到复制一个一样的。我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;buildMultiTargeting&lt;/code&gt; 文件夹里的 targets 文件中写以下内容，这样我们的注意力便可以集中在前面的 targets 文件中了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\buildMultiTargeting\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 直接 Import 我们在 build 中写的那个 targets 文件。
       NuGet 留下了为多框架项目提供特殊扩展的方案，其实有时候也是很有用的。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\build\Walterlv.NuGetTool.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第四部调试&quot;&gt;第四部：调试&lt;/h2&gt;

&lt;p&gt;严格来说，写到这里，我们的跨平台 NuGet 工具已经写完了。在以上状态下，你只需要编译一下，就可以获得一个跨平台的基于 MSBuild Task 的 NuGet 工具。只是——你肯定会非常郁闷——心里非常没谱，这工具到底有没有工作起来！有没有按照我预期的进行工作！如果遇到了 Bug 怎么办！&lt;/p&gt;

&lt;p&gt;于是现在我们来掌握一些调试技巧，这样才方便我们一步步完善我们的功能嘛！&lt;strong&gt;额外插一句：以上第一到第三步几乎都是结构化的步骤，其实非常适合用工具来自动化完成的。&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;让我们的-target-能够正确找到我们新生成的-dll&quot;&gt;让我们的 Target 能够正确找到我们新生成的 dll&lt;/h3&gt;

&lt;p&gt;你应该注意到，我们的 targets 文件在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\build&lt;/code&gt; 目录下，而我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 文件夹下并没有真实的 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt; 文件夹（里面是空的）。于是我们希望在调试状态下，dll 能够指向输出目录下。于是我们修改 targets 文件添加配置：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; $(IsInDemoToolDebugMode) == 'True' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\..\bin\$(Configuration)\netcoreapp2.0\Walterlv.NuGetTool.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; $(IsInDemoToolDebugMode) != 'True' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;$(MSBuildThisFileDirectory)..\tools\netcoreapp2.0\Walterlv.NuGetTool.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetWalterlvToolPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet $(NuGetWalterlvToolPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们就拥有了一个可以供用户设置的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/code&gt; 了。&lt;/p&gt;

&lt;h3 id=&quot;准备一个用于测试此命令行工具的测试项目&quot;&gt;准备一个用于测试此命令行工具的测试项目&lt;/h3&gt;

&lt;p&gt;接着，我们在解决方案中新建一个调试项目 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Debug&lt;/code&gt;（我选用了 .NET Standard 2.0 框架）。然后在它的 csproj 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Import&amp;gt;&lt;/code&gt; 我们刚刚的 .targets 文件，并设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.Debug.csproj --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsInDemoToolDebugMode&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsInDemoToolDebugMode&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.NuGetTool\Assets\build\Walterlv.NuGetTool.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当准备好基本的调试环境之后，我们的解决方案看起来是下面这样的样子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-08-13-03.png&quot; alt=&quot;带有调试环境的解决方案&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;调试命令行项目&quot;&gt;调试命令行项目&lt;/h3&gt;

&lt;p&gt;为了保持根兄弟文章的结构一致，我依然保留了“调试项目”这一部分内容，但其实大家都懂，不是吗？—— &lt;strong&gt;一个控制台程序，谁不会调试啊&lt;/strong&gt;！！！&lt;/p&gt;

&lt;p&gt;但是——如果你希望能够在 MSBuild 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 的环境下调试，就会发现，普通的调试方法并不能得到这样的环境——例如项目特定的参数。&lt;/p&gt;

&lt;p&gt;加一句 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch();&lt;/code&gt; 是最简单的方法了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Program.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 新增了启动调试器的代码。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 时，就会弹出一个调试器选择界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-22-07-18.png&quot; alt=&quot;选择调试器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，也有一些比较正统的方法，为了使这篇文章尽可能简单，我只附一张图，如果有需要，可以自己去尝试：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-12-08-40-14.png&quot; alt=&quot;使用“调试配置”调试&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，即使我们去 Walterlv.Debug 目录下输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令或 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令，也能进入我们的断点了：&lt;/p&gt;

&lt;h2 id=&quot;第五步发挥你的想象力&quot;&gt;第五步：发挥你的想象力&lt;/h2&gt;

&lt;p&gt;想象力是没有限制的，我们只需要在 .targets 文件里面向我们的控制台程序传入合适的参数，即可完成非常多的功能。&lt;/p&gt;

&lt;h3 id=&quot;targets-向控制台程序传参数&quot;&gt;.targets 向控制台程序传参数&lt;/h3&gt;

&lt;p&gt;.targets 向控制台程序传参数只需要按照普通控制台程序传参的方式就可以了：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet $(NuGetWalterlvToolPath) -i $(IntermediateOutputPath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)&lt;/code&gt; 是 msbuild 编译期间会自动设置的全局属性，代表此项目编译过程中临时文件的存放路径（也就是我们常见的 obj 文件夹）。当然，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet msbuild&lt;/code&gt; 也是有这样的全局属性的。&lt;/p&gt;

&lt;p&gt;在 Program.cs 中，只需要解析命令行参数即可接收这样的传参。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Program.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediateOutputPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以尽情发挥你的想象力，传入更多让人意想不到的参数，实现不可思议的功能。更多 MSBuild 全局参数，可以参考我的另一篇文章&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;需要注意，控制台传参数是有字符数量限制的，要解决传参字符数量限制问题，可以参考 &lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E4%BD%BF%E7%94%A8-WriteLinesToFile-%E8%A7%A3%E5%86%B3%E5%8F%82%E6%95%B0%E8%BF%87%E9%95%BF%E6%97%A0%E6%B3%95%E4%BC%A0%E5%85%A5.html&quot;&gt;Roslyn 使用 WriteLinesToFile 解决参数过长无法传入&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;控制台程序向-targets-返回数据&quot;&gt;控制台程序向 .targets 返回数据&lt;/h3&gt;

&lt;p&gt;控制台程序的输出（也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine()&lt;/code&gt; 那个）是能够直接和 MSBuild 的 Target 进行数据交换的。&lt;/p&gt;

&lt;p&gt;有两种不同的方式：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;直接传数据，这些数据可以被捕获成属性或者项，具体可以阅读我的另一篇博客：
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;/post/exec-task-of-msbuild-target&quot;&gt;如何使用 MSBuild Target（Exec）中的控制台输出&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;报告编译警告和编译错误，具体可以阅读我的另一篇博客：
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;/post/standard-error-warning-format&quot;&gt;如何在 MSBuild Target（Exec）中报告编译错误和编译警告&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;使用命令执行完之后的结果&quot;&gt;使用命令执行完之后的结果&lt;/h3&gt;

&lt;p&gt;如果只是传入参数，那么我们顶多只能干一些不痛不痒的事情，我们应该使用我们的控制台程序做一些什么。什么？你说直接去改源代码？那万一你的代码不幸崩溃了，项目岂不被你破坏了！（当然，你去改了源码，还会破坏 MSBuild 的差量编译。）&lt;/p&gt;

&lt;p&gt;所以，我们应该建立一种约定，要求控制台程序生成一些什么，然后在 .targets 里面取出使用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Program.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.NuGetTool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;additionalCompileFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;additionalCompileFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;@&quot;using System;
namespace Walterlv.Debug
{
    public class Doubi
    {
        public string Name { get; }
        private Doubi(string name) =&amp;gt; Name = name;
        public static Doubi Get() =&amp;gt; new Doubi(&quot;&quot;吕毅&quot;&quot;);
    }
}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，我们需要在 .targets 文件里接收这个输出参数。然而命令行调用与 &lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt; 中所写的 Task 不同，命令行调用的后面是不能够立刻应用命令行调用的结果的，因为此时命令还没有结束。&lt;/p&gt;

&lt;p&gt;所以我们需要写一个新的 Target，来使用命令行程序执行后的结果。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet $(NuGetWalterlvToolPath) -i $(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoUseResult&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们生成的 Walterlv.Debug 调试项目在编译完成之后，还会额外多出一个“逗比”类。而且——我们甚至能够直接在 Walterlv.Debug 项目的中使用这个编译中生成的新类。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-23-37-47.png&quot; alt=&quot;可以使用额外生成的类&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用编译生成的新类既不会报错，也不会产生警告下划线，就像原生写的类一样。&lt;/p&gt;

&lt;p&gt;如果你要在编译期间替换一个类而不是新增一个类，例如将 Class1.cs 更换成新类，那么需要将其从编译列表中移除：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Assets\build\Walterlv.NuGetTool.targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Class1.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意：&lt;strong&gt;编译期间才生成的项（&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt;）或者属性（&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt;），需要写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 节点的里面。&lt;/strong&gt;如果写在外面，则不是编译期间生效的，而是始终生效的。当写在外面时，要特别留意可能某些属性没有初始化完全，你应该只使用那些肯定能确认存在的属性或文件。&lt;/p&gt;

&lt;h3 id=&quot;加入差量编译支持&quot;&gt;加入差量编译支持&lt;/h3&gt;

&lt;p&gt;在本文的例子中，当你每次编译时，虽然核心的编译流程不怎么耗时，不过那个命令却是每次都执行。如果你觉得此命令的执行非常耗时，那么建议加入差量编译的支持。关于加入差量编译，可以参考我的另一篇文章&lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;本地测试-nuget-包&quot;&gt;本地测试 NuGet 包&lt;/h3&gt;

&lt;p&gt;在发布 NuGet 包之前，我们可以先在本地安装测试。由于我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\Desktop\Walterlv.NuGetTool\Walterlv.NuGetTool\bin\Debug&lt;/code&gt; 输出路径下已经有了打包好的 nupkg 文件，所以可以加一个本地 NuGet 源。&lt;/p&gt;

&lt;p&gt;我们找一个其他的项目，然后在 Visual Studio 中设置 NuGet 源为我们那个 NuGet 工具项目的输出路径。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-11-23-53-47.png&quot; alt=&quot;设置本地 NuGet 包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时安装，编译完之后，我们就会发现我们的项目生成的 dll 中多出了一个“逗比(Doubi)”类，并且可以在那个项目中编写使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Doubi&lt;/code&gt; 的代码了。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;制作一个跨平台的基于控制台的 NuGet 工具包虽然无关步骤比较多，但总体还算不太难，我们总结一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;准备项目的基本配置（设置各种必要的项目属性）&lt;/li&gt;
  &lt;li&gt;建立好 NuGet 的文件夹结构&lt;/li&gt;
  &lt;li&gt;编写 Target&lt;/li&gt;
  &lt;li&gt;新增功能、调试和测试&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你在实践的过程中遇到了各种问题，欢迎在下面留言，一般我会在一天之内给予回复。&lt;/p&gt;

&lt;p&gt;如果在阅读这篇文章时存在一些概念理解上的问题，或者不知道如何扩展本文的功能，可能需要阅读下我的另一些文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，还有一些正在编写，过一段时间可以阅读到。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/docs.microsoft.com-nuget/blob/master/docs/create-packages/Creating-a-Package.md&quot;&gt;docs.microsoft.com-nuget/Creating-a-Package.md at master · NuGet/docs.microsoft.com-nuget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets?wt.mc_id=MVP&quot;&gt;NuGet pack and restore as MSBuild targets - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.natemcmaster.com/blog/2017/11/11/build-tools-in-nuget/&quot;&gt;Bundling .NET build tools in NuGet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms164309.aspx&quot;&gt;MSBuild Reserved and Well-Known Properties&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/6982575/6233938&quot;&gt;build process - How does MSBuild check whether a target is up to date or not? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms171483.aspx&quot;&gt;How to: Build Incrementally&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/x8zx72cd.aspx&quot;&gt;Exec Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/1367309/6233938&quot;&gt;Overwrite properties with MSBuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/39745383/6233938&quot;&gt;visual studio - How to get property value of a project file using msbuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/davidfowl/NuGetPowerTools&quot;&gt;davidfowl/NuGetPowerTools: A bunch of powershell modules that make it even easier to work with nuget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[MSBuild and Skipping target “&lt;TargetName&gt;&quot; because it has no outputs - Stack Overflow](https://stackoverflow.com/q/27377095/6233938)&lt;/TargetName&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/15012963/6233938&quot;&gt;Don’t include dependencies from packages.config file when creating NuGet package - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/nuget/release-notes/nuget-2.7#Development-Only_Dependencies?wt.mc_id=MVP&quot;&gt;NuGet 2.7 Release Notes - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/Home/issues/4125&quot;&gt;PackageReference should support DevelopmentDependency metadata · Issue #4125 · NuGet/Home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 07 Aug 2019 06:50:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-a-cross-platform-command-based-nuget-tool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-a-cross-platform-command-based-nuget-tool.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>好的框架需要好的 API 设计 —— API 设计的六个原则</title>
        <description>&lt;p&gt;说到框架设计，打心底都会觉得很大很宽泛，而 API 设计是框架设计中的重要组成部分。相比于有很多大佬都认可的&lt;em&gt;面向对象的六大原则&lt;/em&gt;、&lt;em&gt;23 种常见的设计模式&lt;/em&gt;来说，API 设计确实缺少行业公认的原则或者说设计范式。&lt;/p&gt;

&lt;p&gt;不过，没有公认不代表没有。无论是对外提供类库还是提供 url 形式的 API，为了使用者良好的使用体验，依然也是有可以借鉴和参考的经验的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/framework-api-design.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/framework-api-design-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;本文中的 API 设计原则在主要思想上出自 NetBeans 创始人 Jaroslav Tulach 所著的 &lt;em&gt;Practical API Design&lt;/em&gt; 一书；但原书讲述的所有内容很零散，缺乏系统性。所以我们结合了一些开源项目的 API 升级方式对内容进行了整理，形成六个原则。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;api-是什么&quot;&gt;API 是什么？&lt;/h2&gt;

&lt;p&gt;如果要解释 API 这个英文缩写，那一定要说出它的英文原文来：Application Programming Interface，即应用编程接口。虽然&lt;a href=&quot;https://zh.wikipedia.org/zh-hans/%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%8E%A5%E5%8F%A3&quot;&gt;维基百科上有它的定义&lt;/a&gt;，不过还是太复杂了。&lt;/p&gt;

&lt;p&gt;在 .NET 中，我们认为 API 包括了所有公开的类、接口、属性、字段、方法，以及类库提供的配置文件（包括格式）、协议等。&lt;/p&gt;

&lt;h2 id=&quot;api-设计原则&quot;&gt;API 设计原则&lt;/h2&gt;

&lt;p&gt;即便没有学习过任何 API 设计，也没有阅读过设计或重构相关的书籍，只要你有一些编程经验，应该都能够或多或少地评估一组 API 设计得是好是坏。因为——我们都是 API 的使用者，用的 API 多了，也便能体会到各种不同 API 带给我们的不同体验。&lt;/p&gt;

&lt;p&gt;我们团队的几个小伙伴开撕之后，写出了以下文辞：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;我（吕毅）说：&lt;br /&gt;
在 API 设计原则里面，无法写出错误代码是为上策，写错了会出现异常次之，仅靠文档约束为下策，连约束都没有只剩坑的需祭天。&lt;/p&gt;

  &lt;p&gt;头像大人云：&lt;br /&gt;
故上码伐编译，其次伐异常，其次伐文档，其下祭天。祭天之法，为不得已。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以，在下面总结的 API 设计原则中，前面四个都是站在使用者的角度来考虑的。&lt;/p&gt;

&lt;h3 id=&quot;可理解性&quot;&gt;可理解性&lt;/h3&gt;

&lt;p&gt;通常使用者希望使用到某个 API 的时候，为了正确使用这个 API，需要学习一些与这个 API 相关的新知识。而需要新学习的知识越多，我们认为“可理解性”就越低。&lt;/p&gt;

&lt;p&gt;为了提升 API 的可理解性，我们在设计 API 的时候建议考虑这些因素：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果没有必要，不要引入新的概念&lt;/li&gt;
  &lt;li&gt;防止误用
    &lt;ul&gt;
      &lt;li&gt;最好能够避免使用者写出错误的代码（即让错误的代码编译不通过）&lt;/li&gt;
      &lt;li&gt;如果上面那一条有些难度，则建议在运行时抛出异常（使用者便能够明白为什么自己写错了，改怎么更正）&lt;/li&gt;
      &lt;li&gt;另外，最好让错误使用的代码变丑（例如非常冗长难以理解，例如 IDE 会显示下划线警告）&lt;/li&gt;
      &lt;li&gt;不要试图在文档中警告使用者用错了，因为典型的程序员是不看文档的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于防止误用的一个优秀案例，要属单元测试模拟 Moq 了；可以参考 &lt;a href=&quot;https://huangtengxiao.gitee.io/post/Moq%E5%9F%BA%E7%A1%80-%E4%B8%80.html&quot;&gt;Moq 基础系列教程&lt;/a&gt; 并上手编写，体验它对防止误用上做出的努力。&lt;/p&gt;

&lt;h3 id=&quot;可见性&quot;&gt;可见性&lt;/h3&gt;

&lt;p&gt;我们大多数人的开发工具是功能齐全，傻瓜也能使用的 IDE（集成开发环境），这其实是 IDE 可理解性较好的一个体现。&lt;/p&gt;

&lt;p&gt;不过这里要说的是 IDE 的智能感知提示功能；就算没有 IDE，一些常见的代码编辑工具（Visual Studio Code、Sublime、Atom、Notepad++、Vim）也都带有只能感知提示功能。在智能感知提示的帮助下，我们能够在不查阅文档的情况之下了解到当前上下文相关的 API 说明及其简易的使用提示。&lt;/p&gt;

&lt;p&gt;如果我们只通过智能感知提示便能够发现一个新 API 并正确使用它，便可以说这个 API 的可见性是好的。&lt;/p&gt;

&lt;p&gt;典型的例子是实现或者调用某个函数过程：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;实现某个函数的时候，函数的参数类型本来并没有见过，但通过智能感知提示我们能够了解到这个新 API 并正确取到参数中我们期望得到的信息。&lt;/li&gt;
  &lt;li&gt;调用某个函数的时候，我们需要传入本来并没有见过的参数类型，通过智能感知提示，我们能够知道如何构造或获取这些类型然后正确传进去。&lt;/li&gt;
  &lt;li&gt;调用完某个函数后我们得到了返回值，我们本来并没有见过这个类型，但通过智能感知提示，我们能够学习到这个新的类型，并知道如何正确使用这个返回值。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果画一个图来表示较高的可见性和较低的可见性，我想可以画成这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-15-46-00.png&quot; alt=&quot;可见性&quot; /&gt;&lt;br /&gt;
▲ 连接线表示可以通过函数的参数、返回值等得知的新 API&lt;/p&gt;

&lt;p&gt;左侧的 API 没有什么规律，知道什么或者不知道什么全凭经验而定。右侧的 API 从入门 API 开始，可以发现可见性较高的其他相关 API；当更深入地使用后，可能可以发现更高级别（通常也更难正确使用）的 API。&lt;/p&gt;

&lt;p&gt;当然，并不是说可见性越高越好，如果某些 API 是用来完成某些高级功能，或者这个 API 存在较大的性能开销等，为了避免初学者混淆或者误用，应该适当降低其可见性。&lt;/p&gt;

&lt;p&gt;为了更好的可见性，简易在 API 设计的时候：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对于多数常用功能，尽量少提供独立的类；&lt;/li&gt;
  &lt;li&gt;对于高级功能，尽量与简单功能隔离。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;一致性&quot;&gt;一致性&lt;/h3&gt;

&lt;p&gt;当多个相似功能的 API 之间有相似的使用方法时，使用者只需要很少的迁移成本便可以轻松学会新 API 的正确用法。&lt;/p&gt;

&lt;p&gt;比如 LINQ 带来了集合的便捷操作，其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt; 方法用于查找和转换集合每一项的信息。而 LINQ to XML 虽然不是在操作集合而是在操作 XML，但其也有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt; 等方法完成节点的查找和选择。于是，使用者可以通过智能感知提示大致了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;SelectSingleNode&lt;/code&gt; 的基本正确用法。这便是良好的一致性带来的快速入门体验。&lt;/p&gt;

&lt;h3 id=&quot;简单性&quot;&gt;简单性&lt;/h3&gt;

&lt;p&gt;可能有些 API 在经过修改满足了以上可理解性、可见性、一致性之后，极有可能导致一个类或者一组相关类包含了太多方法可用。于是，简单而正确的使用可能就隐藏在众多的 API 中。当然，从面向对象的原则中我们可以说这通常违反了“单一职责原则”。&lt;/p&gt;

&lt;p&gt;简单的任务应该有简单的实现，这是 API 设计中简单性应该做到的。这意味着 API 在提供了灵活的功能之后，建议为常用的任务提供更简单的调用方式。&lt;/p&gt;

&lt;p&gt;例如，&lt;code class=&quot;highlighter-rouge&quot;&gt;InkCanvas&lt;/code&gt; 只需要添加下面这样的 XAML 便可完成书写功能：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;InkCanvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inkCanvas&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;虽然可以进行更多的定制，但是这不是必须的，更多的定制是属于更高级的功能需求的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 以下源码来自 https://docs.microsoft.com/en-us/windows/uwp/design/input/pen-and-stylus-interactions&lt;/span&gt;

 &lt;span class=&quot;c1&quot;&gt;// Set supported inking device types.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inkCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InkPresenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputDeviceTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreInputDeviceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreInputDeviceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Set initial ink stroke attributes.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;InkDrawingAttributes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InkDrawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnorePressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FitToCurve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inkCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InkPresenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateDefaultDrawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;可测性&quot;&gt;可测性&lt;/h3&gt;

&lt;p&gt;API 内部本身需要被测试（单元测试、基准测试等）；然而，API 的使用者也应该具备可测性。&lt;/p&gt;

&lt;p&gt;典型的反例，比如获取某个配置文件的配置信息的方法是静态方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;Config.Get(&quot;SomeKey&quot;)&lt;/code&gt;。那么使用这个 API 的开发者就很难写出能够被单元测试的方法，因为找不到有效的方案来模拟这样的静态方法。&lt;/p&gt;

&lt;h3 id=&quot;兼容性&quot;&gt;兼容性&lt;/h3&gt;

&lt;p&gt;良好的 API 设计利于未来的版本升级——升级带来的用户兼容性成本较低，或者框架开发者的兼容性包袱较轻。&lt;/p&gt;

&lt;p&gt;兼容性有三类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;二进制兼容：更新库后，无需重新编译项目，能够直接运行而不会崩溃。&lt;/li&gt;
  &lt;li&gt;源码兼容：更新库后，可以不用修改项目的源代码可编译通过。&lt;/li&gt;
  &lt;li&gt;功能兼容：更新库后，功能表现依旧和更新之前一样。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了将来的兼容性考虑，设计 API 时建议考虑这些因素：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不要提前公开 API
    &lt;ul&gt;
      &lt;li&gt;如果你的某个 API 是为将来预留的，那么不要开放，因为你不清楚未来的设计需求是怎样的，提前公开的 API 在将来改变的可能性非常高）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;预留足够的扩展点
    &lt;ul&gt;
      &lt;li&gt;没有良好扩展性的 API 通常会因为频繁的需求变更而导致 API 间接变化，这都是兼容性成本。如果在良好的设计下预留了足够的扩展点，那么这样的 API 能够应对未来一段时间内未知的需求变化，使得 API 变化在可控范围内。&lt;/li&gt;
      &lt;li&gt;要预留扩展点就意味着通常应该使用接口或者抽象的概念来描述 API，建议用清晰定位的接口替代具体的类型。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;应该有明确的 API 迁移说明
    &lt;ul&gt;
      &lt;li&gt;如果某个 API 过时了，也不建议删除它；应该标记为过时，并告诉使用者新的 API 是什么。当然如果这个 API 会导致出现不可接受的问题，也可以标记它无法通过编译。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;框架设计&quot;&gt;框架设计&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Practical API Design&lt;/em&gt; 一书认为框架和 API 是等同的。不过从实际行业上的描述来看，框架是更大层面的 API，可以理解为用于完整解决某类问题而开发的一整套 API。&lt;/p&gt;

&lt;p&gt;框架的概念可以很大，也可以很小。&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia&quot;&gt;Avalonia&lt;/a&gt; 可以称为一个跨平台的 UI 框架，这是很大的框架；其中的 &lt;a href=&quot;https://github.com/reactiveui/ReactiveUI&quot;&gt;ReactiveUI&lt;/a&gt; 是一个 UI 响应框架（包含 MVVM）。更小的可以有一套多语言框架、一套依赖注入框架等。&lt;/p&gt;

&lt;p&gt;实践以上总结的六个原则，我们也许能设计出更多优秀的框架。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Practical API Design&lt;/em&gt;, NetBeans 创始人 Jaroslav Tulach 著&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 05 Aug 2019 04:20:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/framework-api-design.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/framework-api-design.html</guid>
        
        
        <category>dotnet</category>
        
        <category>framework</category>
        
      </item>
    
      <item>
        <title>通过 mklink 收集本地文件系统的所有 NuGet 包输出目录来快速调试公共组件代码</title>
        <description>&lt;p&gt;我们做的公共库可能通过 nuget.org 发布，也可能是自己搭建 NuGet 服务器。但是，如果某个包正在开发中，需要快速验证其是否解决掉一些诡异的 bug 的话，除了单元测试这种间接的测试方法，还可以在本地安装未发布的 NuGet 包的方法来快速调试。&lt;/p&gt;

&lt;p&gt;本文介绍如何本地打包发布 NuGet 包，然后通过 mklink 收集所有的本地包达到快速调试的目的。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;将本地文件夹作为-nuget-源&quot;&gt;将本地文件夹作为 NuGet 源&lt;/h2&gt;

&lt;p&gt;我有另一篇博客介绍如何将本地文件夹设置称为 NuGet 包源：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-custom-nuget-source&quot;&gt;全局或为单独的项目添加自定义的 NuGet 源 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 Visual Studio 中打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGet 包管理器&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;包源&lt;/code&gt; 可以直接将一个本地文件夹设置称为 NuGet 包源。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-11-58-37.png&quot; alt=&quot;管理包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其他设置方法可以去那篇博客当中阅读。&lt;/p&gt;

&lt;h2 id=&quot;通过-mklink-收集散落在各处的本地文件夹-nuget-源&quot;&gt;通过 mklink 收集散落在各处的本地文件夹 NuGet 源&lt;/h2&gt;

&lt;p&gt;如下图，是我通过 mklink 将散落在各处的 NuGet 包的调试输出目录收集了起来：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-21-10-56.png&quot; alt=&quot;通过 mklink 收集的 NuGet 包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如，点开其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Packages&lt;/code&gt; 可以看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Packages&lt;/code&gt; 仓库中输出的 NuGet 包：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-21-12-20.png&quot; alt=&quot;其中的一个 NuGet 输出文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于我的每一个文件夹都是指向的 Visual Studio 编译后的输出目录，所以，只需要使用 Visual Studio 重新编译一下项目，文件夹中的 NuGet 包即会更新。&lt;/p&gt;

&lt;p&gt;于是，这相当于我在一个文件夹中，包含了我整个计算机上所有库项目的 NuGet 包，只需要将这个文件夹设置称为 NuGet 包源，即可直接调试本地任何一个公共组件库打出来的 NuGet 包。&lt;/p&gt;

&lt;h2 id=&quot;设置源并体验快速调试&quot;&gt;设置源并体验快速调试&lt;/h2&gt;

&lt;p&gt;如下图，是我将那个收集所有 NuGet 文件夹的目录设置成为了 NuGet 源：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-21-15-42.png&quot; alt=&quot;设置的本地 NuGet 源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，我可以在 Visual Studio 的包管理器中看到所有还没有发布的，依然处于调试状态的各种库：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-21-15-26.png&quot; alt=&quot;各种处于调试状态的各种库&quot; /&gt;&lt;/p&gt;

&lt;p&gt;基于此，我们可以在包还没有编写完的时候调试，验证速度非常快。&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Aug 2019 13:17:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/collect-nuget-output-folder-for-fast-package-debugging.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/collect-nuget-output-folder-for-fast-package-debugging.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>使用 C# 中的 dynamic 关键字调用类型方法时可能遇到的各种问题</title>
        <description>&lt;p&gt;你可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 来定义一个变量或者字段，随后你可以像弱类型语言一样调用这个实例的各种方法，就像你一开始就知道这个类型的所有属性和方法一样。&lt;/p&gt;

&lt;p&gt;但是，使用不当又会遇到各种问题，本文收集使用过程中可能会遇到的各种问题，帮助你解决掉它们。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;快速入门&quot;&gt;快速入门&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 可以这么用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;dynamic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSomeInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问吕毅（lvyi）的博客：blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSomeInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;诡异的东西&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetSomeInstance&lt;/code&gt; 明明返回的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt;，我们却可以调用真实类中的方法。&lt;/p&gt;

&lt;p&gt;接下来讲述使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 过程中可能会遇到的问题和解决方法。&lt;/p&gt;

&lt;h2 id=&quot;编译错误缺少编译器要求的成员&quot;&gt;编译错误：缺少编译器要求的成员&lt;/h2&gt;

&lt;p&gt;你初次在你的项目中引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 关键字后，会出现编译错误，提示 “缺少编译器要求的成员”。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;error CS0656: 缺少编译器要求的成员“Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;对于-net-core-或者-net-standard-项目&quot;&gt;对于 .NET Core 或者 .NET Standard 项目&lt;/h3&gt;

&lt;p&gt;需要为你的项目安装以下两个 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CSharp/&quot;&gt;Microsoft.CSharp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.Dynamic.Runtime/&quot;&gt;System.Dynamic.Runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-14-10-52.png&quot; alt=&quot;引用两个 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是你的项目里面会多出两个引用：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFrameworks&amp;gt;netstandard2.0;net48&amp;lt;/TargetFrameworks&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

      &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;PackageReference Include=&quot;Microsoft.CSharp&quot; Version=&quot;4.5.0&quot; /&amp;gt;
++      &amp;lt;PackageReference Include=&quot;System.Dynamic.Runtime&quot; Version=&quot;4.3.0&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/ItemGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;对于-net-framework-项目&quot;&gt;对于 .NET Framework 项目&lt;/h3&gt;

&lt;p&gt;你需要引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.CSharp&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-14-02-16.png&quot; alt=&quot;添加引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-04-14-04-08.png&quot; alt=&quot;引用 Microsoft.CSharp&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是你的项目里面会多出一项引用：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

      &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Reference Include=&quot;Microsoft.CSharp&quot; /&amp;gt;
&lt;/span&gt;      &amp;lt;/ItemGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;异常0未包含1的定义&quot;&gt;异常：“{0}”未包含“{1}”的定义&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{0}&lt;/code&gt; 是类型名称，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;{1}&lt;/code&gt; 是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 访问的属性或者方法的名称。&lt;/p&gt;

&lt;p&gt;比如，我试图从某个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 中访问到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Key&lt;/code&gt; 属性的时候会抛出以下异常：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:““System.Attribute”未包含“Key”的定义”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;出现此异常的原因是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 所引用的对象里面，没有签名相同的 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt; 的属性或者方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，如果你确认你的类型里面是有这个属性或者方法的话，那么就需要注意需要将此成员改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt; 才可以访问。&lt;/p&gt;

&lt;!-- ## 异常：绑定动态操作时出现异常

中文版异常：

&gt; Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException:“绑定动态操作时出现异常”

英文版异常：

&gt; Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException:&quot;An unexpected exception occurred while binding a dynamic operation&quot;

![绑定动态操作时出现异常](/static/posts/2019-08-04-13-57-00.png) --&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/5678608/6233938&quot;&gt;c# - Why a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException if the invoked method is there? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 04 Aug 2019 06:58:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/problems-when-using-csharp-dymanic-binding.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/problems-when-using-csharp-dymanic-binding.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>设计一个 .NET 可用的弱引用集合（可用来做缓存池使用）</title>
        <description>&lt;p&gt;我们有弱引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt; 可以用来保存可被垃圾回收的对象，也有可以保存键值对的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我们经常会考虑制作缓存池。虽然一般不推荐这么设计，但是你可以使用本文所述的方法和代码作为按垃圾回收缓存的缓存池的设计。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;设计思路&quot;&gt;设计思路&lt;/h2&gt;

&lt;p&gt;既然现有 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt; 可以帮助我们实现弱引用，那么我们可以考虑封装这两个类中的任何一个或者两个来帮助我们完成弱引用集合。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt; 类型仅仅在 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 级别可以访问到集合中的所有的元素，对外开放的接口当中是无法拿到集合中的所有元素的，仅仅能根据 Key 来找到 Value 而已。所以如果要根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt; 来实现弱引用集合那么需要自己记录集合中的所有的 Key，而这样的话我们依然需要自己实现一个用来记录所有 Key 的弱引用集合，相当于鸡生蛋蛋生鸡的问题。&lt;/p&gt;

&lt;p&gt;所以我们考虑直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt; 来实现弱引用集合。&lt;/p&gt;

&lt;p&gt;自己维护一个列表 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;WeakReference&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;，对外开放的 API 只能访问到其中未被垃圾回收到的对象。&lt;/p&gt;

&lt;h2 id=&quot;设计原则&quot;&gt;设计原则&lt;/h2&gt;

&lt;p&gt;在设计此类型的时候，有一个非常大的需要考虑的因素，就是此类型中的元素个数是不确定的，如果设计不当，那么此类型的使用者可能写出的每一行代码都是 Bug。&lt;/p&gt;

&lt;p&gt;你可以参考我的另一篇博客了解设计这种不确定类型的 API 的时候的一些指导：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-principles-of-uncertain-behavior&quot;&gt;如何为非常不确定的行为（如并发）设计安全的 API，使用这些 API 时如何确保安全&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;总结起来就是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;必须提供一个单一的方法，能够完成一些典型场景下某一时刻确定性状态的获取&lt;/li&gt;
  &lt;li&gt;绝不能提供一些可能多次调用获取状态的方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么这个原则怎么体现在此弱引用集合的类型设计上呢？&lt;/p&gt;

&lt;h2 id=&quot;设计实践&quot;&gt;设计实践&lt;/h2&gt;

&lt;h3 id=&quot;分析踩坑&quot;&gt;分析踩坑&lt;/h3&gt;

&lt;h4 id=&quot;ilistt&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IList&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;我们来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;IList&amp;lt;T&amp;gt;&lt;/code&gt; 接口是否可行：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WeakCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsReadOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CopyTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arrayIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;this[]&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Count&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;IsReadOnly&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Contains&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CopyTo&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;IndexOf&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;GetEnumerator&lt;/code&gt; 这些都是在获取状态，&lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Clear&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 是在修改状态，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Insert&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveAt&lt;/code&gt; 会在修改状态的同时读取状态。&lt;/p&gt;

&lt;p&gt;这么多的获取和修改状态的方法，如果提供出去，还指望使用者能够正常使用，简直是做梦！违背以上两个原则。&lt;/p&gt;

&lt;h4 id=&quot;icollectiont&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;那我们看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;IList&amp;lt;T&amp;gt;&lt;/code&gt; 的底层集合 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;，实际上并没有解决问题，所以依然排除不能用！&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class WeakCollection&amp;lt;T&amp;gt; : ICollection&amp;lt;T&amp;gt; where T : class
    {
&lt;span class=&quot;gd&quot;&gt;--      public T this[int index] { get =&amp;gt; throw new NotImplementedException(); set =&amp;gt; throw new NotImplementedException(); }
&lt;/span&gt;        public int Count =&amp;gt; throw new NotImplementedException();
        public bool IsReadOnly =&amp;gt; throw new NotImplementedException();
        public void Add(T item) =&amp;gt; throw new NotImplementedException();
        public void Clear() =&amp;gt; throw new NotImplementedException();
        public bool Contains(T item) =&amp;gt; throw new NotImplementedException();
        public void CopyTo(T[] array, int arrayIndex) =&amp;gt; throw new NotImplementedException();
        public IEnumerator&amp;lt;T&amp;gt; GetEnumerator() =&amp;gt; throw new NotImplementedException();
&lt;span class=&quot;gd&quot;&gt;--      public int IndexOf(T item) =&amp;gt; throw new NotImplementedException();
--      public void Insert(int index, T item) =&amp;gt; throw new NotImplementedException();
&lt;/span&gt;        public bool Remove(T item) =&amp;gt; throw new NotImplementedException();
&lt;span class=&quot;gd&quot;&gt;--      public void RemoveAt(int index) =&amp;gt; throw new NotImplementedException();
&lt;/span&gt;        IEnumerator IEnumerable.GetEnumerator() =&amp;gt; throw new NotImplementedException();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，&lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 方法可能我们会考虑留下来，但这就不能是继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; 了。&lt;/p&gt;

&lt;h4 id=&quot;ienumerablet&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; 里面只有两个方法，看起来少多了，那么我们能用吗？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个方法仅供 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 使用，本来如果只是如此的话，问题还不是很大，但针对 &lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerator&amp;lt;T&amp;gt;&lt;/code&gt; 有一大堆的 Linq 扩展方法，于是这相当于给此弱引用集合提供了大量可以用来读取状态的方法。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这依然非常危险！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;使用者随时可能使用其中一个扩展方法得到了其中一个状态，随后使用另一个扩展方法得知其第二个状态，例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 判断集合中是否存在 IFoo 类型以及是否存在 IBar 类型。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasFoo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weakList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasBar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weakList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对具有并发开发经验的你来说，以上方法第一眼就能识别出这是不正确的写法。然而类型既然已经开放出去给大家使用了，那么这就非常危险。关键是这不是一个并发场景，于是开发者可能更难感受到在同一个上下文中调用两个方法将得到不确定的结果。对于并发可以使用锁，但对于弱引用，没有可以使用的相关方法来快速解决问题。&lt;/p&gt;

&lt;p&gt;因此，&lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; 也是不能继承的。&lt;/p&gt;

&lt;h4 id=&quot;object&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;看来，我们只能继承自单纯的 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 基类了。此类型没有对托管来说可见的状态，于是谁也不会多次读取状态造成状态不确定了。&lt;/p&gt;

&lt;p&gt;因此，我们需要自行实现所有场景下的 API。&lt;/p&gt;

&lt;h3 id=&quot;动手&quot;&gt;动手&lt;/h3&gt;

&lt;p&gt;弱引用集合我们需要这些使用场景：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;向弱引用集合中添加一个元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;此场景下仅仅修改集合而不需要读取任何状态。&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;向弱引用集合中移除一个元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;既然可以在参数中传入元素，说明此元素一定没有会垃圾回收；因此只要集合中还存在此元素，一定可以确定地移除，不会出现不确定的状态。&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;在弱引用集合中找到符合要求的一个或多个元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;一旦满足要求，必须得到完全确定的结果，且在此结果保存的过程中一直生效。&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可选考虑下面这些场景：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;清除所有元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;通常是为了复用某个缓存池的实例。&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一定不能实现下面这些方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;判断是否存在某个元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;因为判断是否存在通常不是单独的操作，通常会使用此集合继续下一个操作，因此一定不能直接提供。&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;其他在本文前面已经喷过不能添加进来的方法&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- 另外，名字也不能叫做 `XxxCollection` 了，因为这会让人觉得这是一个确定的集合。可以参考并发集合中的 `ConcurrentBag` 的命名方式，这是一个容器，里面有不确定的元素。或者干脆按照其使用场景（业务）进行命名，叫做 `XxxMemoryCache`。 --&gt;

&lt;p&gt;于是，我们的 API 设计将是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WeakCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryGetItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;完整代码&quot;&gt;完整代码&lt;/h2&gt;

&lt;p&gt;此类型已经以源代码包的形式发布到了 NuGet 上，你可以安装以下 NuGet 包阅读和使用其源代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Walterlv.Collections.Source&quot;&gt;Walterlv.Collections.Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安装后，你可以在你的项目中使用其源代码，并且可以直接使用 Ctrl + 鼠标点击的方式打开类型的源代码，而不需要进行反编译。&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Aug 2019 06:49:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-weak-collection.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-weak-collection.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何为非常不确定的行为（如并发）设计安全的 API，使用这些 API 时如何确保安全</title>
        <description>&lt;p&gt;.NET 中提供了一些线程安全的类型，如 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConcurrentDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;，它们的 API 设计与常规设计差异很大。如果你对此觉得奇怪，那么正好阅读本文。本文介绍为这些非常不确定的行为设计 API 时应该考虑的原则，了解这些原则之后你会体会到为什么会有这些 API 设计上的差异，然后指导你设计新的类型。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;不确定性&quot;&gt;不确定性&lt;/h2&gt;

&lt;p&gt;像并发集合一样，如 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConcurrentDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ConcurrentQueue&amp;lt;T&amp;gt;&lt;/code&gt;，其设计为线程安全，于是它的每一个对外公开的方法调用都不会导致其内部状态错误。但是，你在调用其任何一个方法的时候，虽然调用的方法本身能够保证其线程安全，能够保证此方法涉及到的状态是确定的，但是一旦完成此方法的调用，其状态都将再次不确定。你只能依靠其方法的返回值来使用刚刚调用那一刻确定的状态。&lt;/p&gt;

&lt;p&gt;我们来看几段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isRunning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 当前已经在执行队列，因此无需继续执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这两段代码都使用到了可能涉及线程安全的一些代码。前者使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Interlocked&lt;/code&gt; 做原则操作，而后者使用并发字典。&lt;/p&gt;

&lt;p&gt;无论写上面哪一段代码，都面临着问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;此刻调用的那一句话得到的任何结果都仅仅只表示这一刻，而不代表其他任何代码时的结果。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;比如前者的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Interlocked.CompareExchange(ref _isRunning, 1, 0)&lt;/code&gt; 我们得到一个返回值 &lt;code class=&quot;highlighter-rouge&quot;&gt;isRunning&lt;/code&gt;，然后判断这个返回值。但是我们绝对不能够判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;_isRunning&lt;/code&gt; 这个字段，因为这个字段非常易变，在你的任何一个代码上下文中都可能变成你不希望看到的值。&lt;code class=&quot;highlighter-rouge&quot;&gt;Interlocked&lt;/code&gt; 是原子操作，所以才确保安全。&lt;/p&gt;

&lt;p&gt;而后者，此时访问得到的字典数据，和下一时刻访问得到的字典数据将可能完全不匹配，两次的数据不能通用。&lt;/p&gt;

&lt;h2 id=&quot;api-用法指导&quot;&gt;API 用法指导&lt;/h2&gt;

&lt;p&gt;如果你正在为一个易变的状态设计 API，或者说你需要编写的类型带有很强的不确定性（类型状态的变化可能发生在任何一行代码上），那么你需要遵循一些设计原则才能确保安全。&lt;/p&gt;

&lt;h3 id=&quot;同一个上下文仅能查看或修改一次状态&quot;&gt;同一个上下文仅能查看或修改一次状态&lt;/h3&gt;

&lt;p&gt;比如要为缓存设计一个获取可用实例的方法，可以使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// CreateCachedInstance 是一个工厂方法，所有 GetOrAdd 的地方都是用此工厂方法创建。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetOrAdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateCachedInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是绝对不能使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;KeyValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateCachedInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这一段代码就是对并发的状态 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyValues&lt;/code&gt; 做了两次访问。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConcurrentDictionary&lt;/code&gt; 也正是考虑到了这种设计场景，于是才提供了 API &lt;code class=&quot;highlighter-rouge&quot;&gt;GetOrAdd&lt;/code&gt; 方法。让你在获取对象实例的时候可以通过工厂方法去创建实例。&lt;/p&gt;

&lt;p&gt;如果你需要设计这种状态极易变的 API，那么需要针对一些典型的设计场景提供一次调用就能获取此时此刻所有状态的方法。就像上文的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetOrAdd&lt;/code&gt; 一样。&lt;/p&gt;

&lt;p&gt;另一个例子，&lt;code class=&quot;highlighter-rouge&quot;&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt; 弱引用对象的管理也是在一个方法里面可以获取到一个绝对确定的状态，而避免使用方进行两次判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一旦这里拿到了对象，这个对象一定会存在且可用。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一定不能提供两个方法调用来完成这样的事情（比如先判断是否存在再获取对象的实例，就像 .NET Framework 4.0 和早期版本弱引用的 API 设计一样）。&lt;/p&gt;

&lt;h3 id=&quot;对于并发如果有多次查看或者修改状态必须加锁&quot;&gt;对于并发，如果有多次查看或者修改状态，必须加锁&lt;/h3&gt;

&lt;p&gt;比如以下方法，是试图一个接一个地依次执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;_queue&lt;/code&gt; 中的所有任务。&lt;/p&gt;

&lt;p&gt;虽然我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Interlocked.CompareExchange&lt;/code&gt; 原子操作，但因为后面依然涉及到了多次状态的获取，导致不得不加锁才能确保安全。我们依然使用原则操作是为了避免单纯 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 带来的性能损耗。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isRunning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_queue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConcurrentQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isRunning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_isRunning&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 当前已经在执行队列，因此无需继续执行。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 当前还没有任何队列开始执行，因此需要开始执行队列。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryDequeue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 内部已包含异常处理，因此外面可以无需捕获或者清理。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;hasTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryPeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_isRunning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的完全解读：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;当执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法的时候，先判断当前是否已经在跑其他的任务：
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isRunning&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 表示当前一定没有在跑其他任务，我们使用原则操作立刻将其修改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;；&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isRunning&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 表示当前不确定是否在跑其他任务；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;既然 &lt;code class=&quot;highlighter-rouge&quot;&gt;isRunning&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 的时候状态不确定，于是我们加锁来判断其是否真的有任务在跑：
    &lt;ul&gt;
      &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 环境中确认 &lt;code class=&quot;highlighter-rouge&quot;&gt;_isRunning&lt;/code&gt; 字段而非变量为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 则说明真的有任务在跑，此时等待任务完成即可，这里就可以退出了；&lt;/li&gt;
      &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 环境中发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;_isRunning&lt;/code&gt; 字段而非变量为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 则说明实际上是没有任务在跑的（刚刚判断为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 只是因为这两次判断之间，并发的任务刚刚在结束的过程中），于是需要跟一开始判断为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 一样，进入到后面的循环中；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;外层的 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环第一次是一定能进去的，于是我们暂且不谈；&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 内循环中，我们依次检查并发队列 &lt;code class=&quot;highlighter-rouge&quot;&gt;_queue&lt;/code&gt; 中是否还有任务要执行，如果有要执行的，就执行：
    &lt;ul&gt;
      &lt;li&gt;这个过程我们完全没有做加锁，因为这可能是非常耗时的任务，如果我们加锁，将导致其他线程出现非常严重的资源浪费；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;queue&lt;/code&gt; 中的所有任务执行完毕，我们将进入一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 区间：
    &lt;ul&gt;
      &lt;li&gt;在这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 区间里面我们再次确认任务是否已经完成，如果没有完成，我们靠最外层的 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环重新回到内层 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环中继续任务；&lt;/li&gt;
      &lt;li&gt;如果在这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 区间里面我们发现任务已经完成了，就设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;_isRunning&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;，表示任务真的已经完成，随后退出 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你可以注意到我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 是用来确认一开始 &lt;code class=&quot;highlighter-rouge&quot;&gt;isRunning&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; 时的那个不确定的状态的。因为我们需要多次访问这个状态，所以必须加锁来确认状态是同步的。&lt;/p&gt;

&lt;h2 id=&quot;api-设计指导&quot;&gt;API 设计指导&lt;/h2&gt;

&lt;p&gt;在了解了上面的用法指导后，API 设计指导也呼之欲出了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;针对典型的应用场景，必须设计一个专门的方法，一次调用即可完全获取当时需要的状态，或者一次调用即可完全修改需要修改的状态；&lt;/li&gt;
  &lt;li&gt;不要提供大于 1 个方法组合在一起才能使用的 API，这会让调用方获取不一致的状态。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于多线程并发导致的不确定性，使用方虽然可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 来规避以上第二条问题，但设计方最好在设计之初就避免问题，以便让 API 更好使用。&lt;/p&gt;

&lt;p&gt;关于通用 API 设计指导，你可以阅读我的另一篇双语博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/framework-api-design&quot;&gt;好的框架需要好的 API 设计 —— API 设计的六个原则 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 03 Aug 2019 05:56:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/design-principles-of-uncertain-behavior.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/design-principles-of-uncertain-behavior.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>通过 AppSwitch 禁用 WPF 内置的触摸让 WPF 程序可以处理 Windows 触摸消息</title>
        <description>&lt;p&gt;WPF 框架自己实现了一套触摸机制，但同一窗口只能支持一套触摸机制，于是这会禁用系统的触摸消息（&lt;code class=&quot;highlighter-rouge&quot;&gt;WM_TOUCH&lt;/code&gt;）。这能够很大程度提升 WPF 程序的触摸响应速度，但是很多时候又会产生一些 Bug。&lt;/p&gt;

&lt;p&gt;如果你有需要，可以考虑禁用 WPF 的内置的实时触摸（RealTimeStylus）。本文介绍禁用方法，使用 AppSwitch，而不是网上广为流传的反射方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何设置-appswitch&quot;&gt;如何设置 AppSwitch&lt;/h2&gt;

&lt;p&gt;在你的应用程序的 app.config 文件中加入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Switch.System.Windows.Input.Stylus.DisableStylusAndTouchSupport=true&lt;/code&gt; 开关，即可关闭 WPF 内置的实时触摸，而改用 Windows 触摸消息（&lt;code class=&quot;highlighter-rouge&quot;&gt;WM_TOUCH&lt;/code&gt;）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;runtime&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppContextSwitchOverrides&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Switch.System.Windows.Input.Stylus.DisableStylusAndTouchSupport=true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/runtime&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你的解决方案中没有找到 app.config 文件，可以创建一个：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-01-19-08-18.png&quot; alt=&quot;新建文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-08-01-19-08-50.png&quot; alt=&quot;应用程序配置文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，把上面的代码拷贝进去即可。&lt;/p&gt;

&lt;h2 id=&quot;反射禁用的方法&quot;&gt;反射禁用的方法&lt;/h2&gt;

&lt;p&gt;微软的官方文档也有提到使用放射禁用的方法，但一般不推荐这种调用内部 API 的方式，比较容易在 .NET 的版本更新中出现问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/disable-the-realtimestylus-for-wpf-applications&quot;&gt;Disable the RealTimeStylus for WPF Applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/wpf-%E7%A6%81%E7%94%A8%E5%AE%9E%E6%97%B6%E8%A7%A6%E6%91%B8&quot;&gt;WPF 禁用实时触摸 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;此方法可以解决的问题一览&quot;&gt;此方法可以解决的问题一览&lt;/h2&gt;

&lt;h3 id=&quot;拖拽窗口或者调整窗口大小时不能实时跟随的问题&quot;&gt;拖拽窗口或者调整窗口大小时不能实时跟随的问题&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/56354510/why-all-my-wpf-applications-fail-to-drag-outside-of-their-windows-since-windows&quot;&gt;Why all my WPF applications fail to drag outside of their windows since Windows 10 (1809/1903) such as resizing the window or do drag drop? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/issues/1323&quot;&gt;All WPF applications fail to drag outside of their windows since Windows 10 (1809/1903) such as resizing the window or do drag drop · Issue #1323 · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://i.stack.imgur.com/LZA4h.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在部分设备上启动即崩溃&quot;&gt;在部分设备上启动即崩溃&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/dotnet/issues/480&quot;&gt;.NET 4.7 - WPF - Touch Enabled Devices Crash Applications · Issue #480 · microsoft/dotnet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/55303/visual-studio-may-terminate-unexpectedly-when-runn.html&quot;&gt;Visual Studio may freeze or crash when running on a pen-enabled machine - Developer Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;在透明窗口上触摸会挡住-uwp-程序&quot;&gt;在透明窗口上触摸会挡住 UWP 程序&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/50382605/on-windows-10-1803-all-applications-lost-touch-or-stylus-if-a-wpf-transparent&quot;&gt;c# - On Windows 10 (1803), all applications lost touch or stylus if a WPF transparent window covers on them - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/disable-the-realtimestylus-for-wpf-applications&quot;&gt;Disable the RealTimeStylus for WPF Applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/WPF-Samples/blob/master/Compatibility/runtimeconfig.template.json&quot;&gt;WPF-Samples/runtimeconfig.template.json at master · microsoft/WPF-Samples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/issues/1323&quot;&gt;All WPF applications fail to drag outside of their windows since Windows 10 (1809/1903) such as resizing the window or do drag drop · Issue #1323 · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 01 Aug 2019 12:56:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-disable-stylus-and-touch-support.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-disable-stylus-and-touch-support.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Visual Studio 2019 中使用 .NET Core 预览版 SDK 的全局配置文件在哪里？</title>
        <description>&lt;p&gt;本文介绍在使用 Visual Studio 2019 或者命令行执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令时，决定是否使用 .NET Core SDK 预览版的全局配置文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;指定是否使用 .NET Core 预览版 SDK 的全局配置文件在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%\Microsoft\VisualStudio\16.0_xxxxxxxx\sdk.txt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%&lt;/code&gt; 是 Windows 系统中自带的环境变量，&lt;code class=&quot;highlighter-rouge&quot;&gt;16.0_xxxxxxxx&lt;/code&gt; 在不同的 Visual Studio 版本下不同。&lt;/p&gt;

&lt;p&gt;比如，我的路径就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\AppData\Local\Microsoft\VisualStudio\16.0_0b1a4ea6\sdk.txt&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这个文件的内容非常简单，只有一行：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UsePreviews=True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-37-10.png&quot; alt=&quot;sdk.txt 的所在路径&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你一定觉得奇怪，我们在 Visual Studio 2019 中设置了使用 .NET Core SDK 预览版之后，这个配置是全局生效的，即便在命令行中运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 也是会因此而使用预览版或者正式版的。但是这个路径明显看起来是 Visual Studio 的私有路径。&lt;/p&gt;

&lt;p&gt;虽然这很诡异，但确实如此，不信，可以看我是如何确认这个文件就是 .NET Core SDK 预览版的全局配置的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/find-out-the-dotnet-sdk-preview-config-file&quot;&gt;找出 .NET Core SDK 是否使用预览版的全局配置文件在那里（探索篇）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，如果你想知道如何在 Visual Studio 2019 中指定使用 .NET Core SDK 的预览版，可以参考我的另外一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-set-dotnet-core-sdk-preview-in-visual-studio&quot;&gt;如何在 Visual Studio 2019 中设置使用 .NET Core SDK 的预览版（全局生效）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-00-09.png&quot; alt=&quot;Visual Studio 2019 的&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Jul 2019 09:47:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/where-is-the-dotnet-sdk-preview-config-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/where-is-the-dotnet-sdk-preview-config-file.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>The partial same C# namespace may cause source code compatibility issue</title>
        <description>&lt;p&gt;You might just add some simple APIs in your library and you’ll not think that will break down your compatibility. But actually, it might, that is – the source-code compatibility.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/source-code-compatibility-issue-of-adding-apis.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/source-code-compatibility-issue-of-adding-apis-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;Assume that we’ve written a project P which references another two libraries A and B. And we have a &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.A.Diagnostics.Foo&lt;/code&gt; class in library A.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now we add a new class &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.B.Diagnostics.Bar&lt;/code&gt; class into the B library. That is adding a new API only.&lt;/p&gt;

&lt;p&gt;Unfortunately, the code above would fail to compile because of the ambiguity of &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnostics&lt;/code&gt; namespace. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; class cannot be found in an ambiguity namespace.&lt;/p&gt;

&lt;p&gt;I write this post down to tell you that there may be source-code compatibility issue even if you only upgrade your library by simply adding APIs.&lt;/p&gt;
</description>
        <pubDate>Sat, 27 Jul 2019 12:48:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/source-code-compatibility-issue-of-adding-apis-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/source-code-compatibility-issue-of-adding-apis-en.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>使用基于 Roslyn 的 Microsoft.CodeAnalysis.PublicApiAnalyzers 来追踪项目的 API 改动，帮助保持库的 API 兼容性</title>
        <description>&lt;p&gt;做库的时候，需要一定程度上保持 API 的兼容性&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步安装-nuget-包&quot;&gt;第一步：安装 NuGet 包&lt;/h2&gt;

&lt;p&gt;首先打开你的库项目，或者如果你希望从零开始也可以直接新建一个项目。这里为了博客阅读的简单，我创建一个全新的项目来演示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-15-58-05.png&quot; alt=&quot;打开一个项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，为主要的库项目安装 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.PublicApiAnalyzers&quot;&gt;NuGet Gallery - Microsoft.CodeAnalysis.PublicApiAnalyzers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-15-59-10.png&quot; alt=&quot;安装 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;安装完成之后，你的项目文件（.csproj）可能类似于下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.CodeAnalysis.PublicApiAnalyzers&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.9.3&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第二步创建-api-记录文件&quot;&gt;第二步：创建 API 记录文件&lt;/h2&gt;

&lt;p&gt;在你的项目内创建两个文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;PublicAPI.Shipped.txt&lt;/li&gt;
  &lt;li&gt;PublicAPI.Unshipped.txt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-01-09.png&quot; alt=&quot;创建 API 记录文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这就是两个普通的文本文件。创建纯文本文件的方法是在项目上右键 -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;添加&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建项...&lt;/code&gt;，然后在打开的模板中选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;文本文件&lt;/code&gt;，使用上面指定的名称即可（要创建两个）。&lt;/p&gt;

&lt;p&gt;然后，编辑项目文件，我们需要将这两个文件加入到项目中来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-13-59.png&quot; alt=&quot;编辑项目文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你看不到上图中的“编辑项目文件”选项，则需要升级项目文件到 SDK 风格，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然后，将这两个文件添加为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AdditionalFiles&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

    &amp;lt;PropertyGroup&amp;gt;
      &amp;lt;TargetFramework&amp;gt;netstandard2.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;PackageReference Include=&quot;Microsoft.CodeAnalysis.PublicApiAnalyzers&quot; Version=&quot;2.9.3&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;

+   &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;AdditionalFiles Include=&quot;PublicAPI.Shipped.txt&quot; /&amp;gt;
+     &amp;lt;AdditionalFiles Include=&quot;PublicAPI.Unshipped.txt&quot; /&amp;gt;
+   &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;
  &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你把这两个文件放到了其他的路径，那么上面也需要改成对应的路径。&lt;/p&gt;

&lt;p&gt;这时，这两个文件内容还是空的。&lt;/p&gt;

&lt;h2 id=&quot;第三步添加-api-记录&quot;&gt;第三步：添加 API 记录&lt;/h2&gt;

&lt;p&gt;这个时候，你会看到库中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt; 类、方法、属性等都会发出修改建议，说此符号并不是已声明 API 的一部分。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-27-38.png&quot; alt=&quot;类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-28-51.png&quot; alt=&quot;属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击小灯泡，即可将点击所在的 API 加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublicAPI.Unshipped.txt&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;我将两个 API 都添加之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;PublicAPI.Unshipped.txt&lt;/code&gt; 文件中现在是这样的（注意有一个隐式构造函数哦）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Walterlv.PackageDemo.ApiTracking.Class1
Walterlv.PackageDemo.ApiTracking.Class1.Class1() -&amp;gt; void
Walterlv.PackageDemo.ApiTracking.Class1.Foo.get -&amp;gt; string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;体验-api-的追踪&quot;&gt;体验 API 的追踪&lt;/h2&gt;

&lt;p&gt;现在，我们将 Foo 属性改名成 Foo2 属性，于是就会出现编译警告：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-45-51.png&quot; alt=&quot;编译警告&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;RS0016 Symbol ‘Foo2.get’ is not part of the declared API.&lt;br /&gt;
RS0017 Symbol ‘Walterlv.PackageDemo.ApiTracking.Class1.Foo.get -&amp;gt; string’ is part of the declared API, but is either not public or could not be found&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;提示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo2&lt;/code&gt; 属性不是已声明 API 的一部分，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 属性虽然是已声明 API 的一部分，但已经找不到了。&lt;/p&gt;

&lt;p&gt;这种提示对于保持库的兼容性是非常有帮助的。&lt;/p&gt;

&lt;h2 id=&quot;将警告变成错误&quot;&gt;将警告变成错误&lt;/h2&gt;

&lt;p&gt;在分析器的规则上面右键，可以为某项规则设置严重性。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-44-27.png&quot; alt=&quot;将警告设置为错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，再编译即会报告编译错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-16-49-28.png&quot; alt=&quot;编译错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;项目中也会多一个规则集文件：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

    &amp;lt;PropertyGroup&amp;gt;
      &amp;lt;TargetFramework&amp;gt;netstandard2.0&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;CodeAnalysisRuleSet&amp;gt;Walterlv.PackageDemo.ApiTracking.ruleset&amp;lt;/CodeAnalysisRuleSet&amp;gt;
&lt;/span&gt;    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;PackageReference Include=&quot;Microsoft.CodeAnalysis.PublicApiAnalyzers&quot; Version=&quot;2.9.3&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;AdditionalFiles Include=&quot;PublicAPI.Shipped.txt&quot; /&amp;gt;
      &amp;lt;AdditionalFiles Include=&quot;PublicAPI.Unshipped.txt&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;

  &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第四步将-api-打包&quot;&gt;第四步：将 API 打包&lt;/h2&gt;

&lt;p&gt;前面我们都是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublicAPI.Unshipped.txt&lt;/code&gt; 文件中追踪 API。但是如果我们的库需要发布一个版本的时候，我们就需要跟上一个版本比较 API 的差异。&lt;/p&gt;

&lt;p&gt;上一个发布版本的 API 就记录在 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublicAPI.Shipped.txt&lt;/code&gt; 文件中，这两个文件的差异即是这两个版本的 API 差异。在一个新的版本发布后，就需要将 API 归档到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublicAPI.Shipped.txt&lt;/code&gt; 文件中。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn-analyzers/blob/master/src/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md&quot;&gt;roslyn-analyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md at master · dotnet/roslyn-analyzers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 27 Jul 2019 08:54:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/track-api-changes-using-roslyn-public-api-analyzers.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/track-api-changes-using-roslyn-public-api-analyzers.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>找出 .NET Core SDK 是否使用预览版的全局配置文件在哪里（探索篇）</title>
        <description>&lt;p&gt;你是否好奇 Visual Studio 2019 中的 .NET Core SDK 预览版开关是全局生效的，那个全局的配置在哪里呢？&lt;/p&gt;

&lt;p&gt;本文将和你一起探索找到这个全局的配置文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-process-monitor-探索&quot;&gt;使用 Process Monitor 探索&lt;/h2&gt;

&lt;h3 id=&quot;下载-process-monitor&quot;&gt;下载 Process Monitor&lt;/h3&gt;

&lt;p&gt;Process Monitor 是微软极品工具箱的一部分，你可以在此页面下载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/sysinternals/downloads/procmon&quot;&gt;Process Monitor - Windows Sysinternals - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;打开-process-monitor&quot;&gt;打开 Process Monitor&lt;/h3&gt;

&lt;p&gt;当你一开始打开 Process Monitor 的时候，列表中会立刻刷出大量的进程的操作记录。这么多的记录会让我们找到目标进程操作的文件有些吃力，于是我们需要设置规则。&lt;/p&gt;

&lt;p&gt;Process Monitor 的工具栏按钮并不多，而且我们这一次的目标只会用到其中的两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;清除列表（将已经记录的所有数据清空，便于聚焦到我们最关心的数据中）&lt;/li&gt;
  &lt;li&gt;设置过滤器（防止大量无关的进程操作进入列表中干扰我们的查找）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-01-13-36-35.png&quot; alt=&quot;Process Monitor 的工具栏按钮&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;设置过滤规则&quot;&gt;设置过滤规则&lt;/h3&gt;

&lt;p&gt;在工具栏上点击“设置过滤器”，然后，添加我们感兴趣的两个进程名称：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;devenv.exe&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild.exe&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前者是 Visual Studio 的进程名，后者是 MSBuild.exe 的进程名。我们使用这两个进程名称分别找到 Visual Studio 2019 是如何设置全局 .NET Core 预览配置的，并且在命令行中运行 MSBuild.exe 来验证确实是这个全局配置。&lt;/p&gt;

&lt;p&gt;然后排除除了文件意外的所有事件类型，最终是如下过滤器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-10-09-14.png&quot; alt=&quot;设置过滤器&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;捕获-devenvexe&quot;&gt;捕获 devenv.exe&lt;/h3&gt;

&lt;p&gt;现在，我们打开 Visual Studio 2019，然后停留到下面这个界面中。改变一下 .NET Core SDK 预览版选项的勾选状态。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-09-00-09.png&quot; alt=&quot;设置 Visual Studio 2019 使用 .NET Core SDK 预览版&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们点击一下“确定”，将立即可以在 Process Monitor 中看到一些文件的修改：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-10-24-15.png&quot; alt=&quot;捕获到的文件修改&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上面是在点击“确定”按钮一瞬间 Visual Studio 2019 的所有文件操作。你可以注意到左侧的时间，我的截图中从 45 秒到 48 秒是可能有效的文件读写，再后面已经延迟了 10 秒了，多半是其他的操作。&lt;/p&gt;

&lt;p&gt;在这些文件中，可以很明显看到文件分为三类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 一个不知名的文件，但似乎跟我们的 .NET Core SDK 相关&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SettingsLogs&lt;/code&gt; 一看就是给设置功能用的日志&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;VSApplicationInsights&lt;/code&gt; 一看就是数据收集相关&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过排除法，我们能得知最关键的文件就是那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt;。去看一看那个文件的内容，发现只有一行：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UsePreviews=True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这基本上可以确认 Visual Studio 2019 设置是否使用 .NET Core SDK 预览版就是在这个文件中。&lt;/p&gt;

&lt;p&gt;不过，这带来一个疑惑，就是这个路径特别不像是 .NET Core SDK 的配置路径，倒像是 Visual Studio 自己的设置配置。&lt;/p&gt;

&lt;p&gt;于是必须通过其他途径来确认这是否就是真实的全局配置。&lt;/p&gt;

&lt;h3 id=&quot;捕获-msbuildexe&quot;&gt;捕获 MSBuild.exe&lt;/h3&gt;

&lt;p&gt;现在，我们清除一下 Process Monitor 中的已经记录的数据，然后，我们在命令行中对一个项目敲下 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后在 Process Monitor 里面观察事件。这次发现事件相当多，于是换个方式。&lt;/p&gt;

&lt;p&gt;因为我们主要是验证 &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 文件，但同时希望看看是否还有其他文件。于是我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 文件相关的事件高亮。&lt;/p&gt;

&lt;p&gt;点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Filter&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Highlight...&lt;/code&gt;，然后选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;contains&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 时则 &lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-12-58-58.png&quot; alt=&quot;打开 Highlight&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-12-58-25.png&quot; alt=&quot;高亮 sdk.txt 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，再看捕获到的事件，可以发现编译期间确实读取了这个文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-13-00-23.png&quot; alt=&quot;MSBuild.exe 读取了 sdk.txt&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此举虽不能成为此文件是全局配置的铁证，但至少说明这个文件与全局配置非常相关。&lt;/p&gt;

&lt;p&gt;另外，继续在记录中翻找，还可以发现与此配置可能相关的两个 dll：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft.Build.NuGetSdkResolver.dll&lt;/li&gt;
  &lt;li&gt;Microsoft.DotNet.MSBuildSdkResolver.dll&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-13-02-48.png&quot; alt=&quot;可能与此相关的 dll&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;验证结论&quot;&gt;验证结论&lt;/h3&gt;

&lt;p&gt;要验证此文件确实是全局配置其实也很简单，自行改一改配置，然后使用 MSBuild.exe 编译试试即可。&lt;/p&gt;

&lt;p&gt;现在，将 sdk.txt 文件内容改为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UsePreviews=False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译一下使用了 .NET Core 3.0 新特性的项目（我使用了 Microsoft.NET.Sdk.WindowsDesktop，这是 3.0 才有的 SDK）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-13-07-51.png&quot; alt=&quot;不使用预览版编译&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编译错误，提示 Microsoft.NET.Sdk.WindowsDesktop 这个 SDK 没有找到。&lt;/p&gt;

&lt;p&gt;现在，将 sdk.txt 文件内容改为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UsePreviews=True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译相同的项目，发现可以正常编译通过了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-27-13-09-39.png&quot; alt=&quot;使用预览版编译&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这可以证明，此文件正是决定是否使用预览版的决定性证据。&lt;/p&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;但值得注意的是，打开 Visual Studio 2019 后，发现其设置界面并没有应用此文件最新的修改，这可以说 Visual Studio 2019 的配置是不止这一处。&lt;/p&gt;

&lt;h2 id=&quot;反编译探索&quot;&gt;反编译探索&lt;/h2&gt;

&lt;p&gt;通过反编译探索的方式感谢小伙伴 &lt;a href=&quot;https://github.com/KodamaSakuno&quot;&gt;KodamaSakuno (神樹桜乃)&lt;/a&gt; 彻夜寻找。&lt;/p&gt;

&lt;p&gt;相关的代码在 &lt;a href=&quot;https://github.com/dotnet/cli/blob/master/src/Microsoft.DotNet.MSBuildSdkResolver/VSSettings.cs&quot;&gt;cli/VSSettings.cs at master · dotnet/cli&lt;/a&gt; 中，你可以前往查看。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;VSSettings&lt;/code&gt; 的构造函数中，为字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;_settingsFilePath&lt;/code&gt; 赋值，拼接了 &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 文件的路径。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_settingsFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFolderPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpecialFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LocalApplicationData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;VisualStudio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Major&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.0_&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instanceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;sdk.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;读取时，使用此路径下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;sdk.txt&lt;/code&gt; 文件读取了 &lt;code class=&quot;highlighter-rouge&quot;&gt;UsePreviews&lt;/code&gt; 变量的值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadFromDisk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_settingsFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexOfEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'='&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexOfEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexOfEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexOfEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexOfEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UsePreviews&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usePreviews&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_disallowPrerelease&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usePreviews&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// File does not have UsePreviews entry -&amp;gt; use default&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_disallowPrerelease&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_disallowPrereleaseByDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 27 Jul 2019 06:57:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/find-out-the-dotnet-sdk-preview-config-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/find-out-the-dotnet-sdk-preview-config-file.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>nuget.exe 还原解决方案 NuGet 包的时候出现错误：调用的目标发生了异常。Error parsing the nested project section in solution file.</title>
        <description>&lt;p&gt;我这里使用 Visual Studio 2019 能好好编译的一个项目，发现在另一个小伙伴那里却编译不通过，是在 NuGet 还原那里报告了错误：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;调用的目标发生了异常。Error parsing the nested project section in solution file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文介绍如何解决这样的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;此问题的原因可能有多种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;解决方案里面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt; 不成对，导致某个项目没有被识别出来&lt;/li&gt;
  &lt;li&gt;解决方案中 Global 部分的项目 Id 没有在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 部分发现对应的项目&lt;/li&gt;
  &lt;li&gt;解决方案里面出现了当前 MSBuild 版本不认识的项目类型&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;h3 id=&quot;project-和-endproject-不成对&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt; 不成对&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt; 不成对通常是合并分支时，自动解冲突解错了导致的，例如像下面这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo2&quot;, &quot;Walterlv.Demo2\Walterlv.Demo2.csproj&quot;, &quot;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&quot;
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而解决方法，就是补全缺失的 &lt;code class=&quot;highlighter-rouge&quot;&gt;EndProject&lt;/code&gt; 行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
&lt;span class=&quot;gi&quot;&gt;++  EndProject
&lt;/span&gt;    Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo2&quot;, &quot;Walterlv.Demo2\Walterlv.Demo2.csproj&quot;, &quot;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&quot;
    EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;global-部分的项目-id-没有在-project-部分发现对应的项目&quot;&gt;Global 部分的项目 Id 没有在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 部分发现对应的项目&lt;/h3&gt;

&lt;p&gt;这是说，如果在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Global&lt;/code&gt; 部分通过项目 Id 引用了一些项目，但是这些项目没有在前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 部分定义。例如下面的 sln 片段：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo2&quot;, &quot;Walterlv.Demo2\Walterlv.Demo2.csproj&quot;, &quot;{98FF9756-B95A-4FDB-9858-5106F486FBF3}&quot;
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{98FF9756-B95A-4FDB-9858-5106F486FBF3}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(NestedProjects) = preSolution
&lt;span class=&quot;gd&quot;&gt;--  		{DC0B1D44-5DF4-4590-BBFE-072183677A78} = {20B61509-640C-492B-8B33-FB472CCF1391}
&lt;/span&gt;    		{98FF9756-B95A-4FDB-9858-5106F486FBF3} = {20B61509-640C-492B-8B33-FB472CCF1391}
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {F2F1AD1B-207B-4731-ABEB-92882F89B155}
    	EndGlobalSection
    EndGlobal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面红框标注的项目 Id &lt;code class=&quot;highlighter-rouge&quot;&gt;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&lt;/code&gt; 在前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 部分是没有定义的，于是出现问题。这通常也是合并冲突所致。&lt;/p&gt;

&lt;p&gt;解决方法是删掉这个多于的配置，或者在前面加回误删的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 节点，如：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project(&quot;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&quot;) = &quot;Walterlv.Demo&quot;, &quot;Walterlv.Demo\Walterlv.Demo.csproj&quot;, &quot;{DC0B1D44-5DF4-4590-BBFE-072183677A78}&quot;
EndProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;出现了当前-msbuild-版本不认识的项目类型&quot;&gt;出现了当前 MSBuild 版本不认识的项目类型&lt;/h3&gt;

&lt;p&gt;可能是 nuget 识别出来的 MSBuild 版本过旧，也可能是没有安装对应的工作负载。&lt;/p&gt;

&lt;p&gt;检查你的项目是否安装了需要的工作负载，比如做 Visual Studio 插件开发需要插件工作负载。可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-prepare-visual-studio-extension-development-environment&quot;&gt;如何安装和准备 Visual Studio 扩展/插件开发环境 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我在另外的博客中写了解决方案中项目类型的内容：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-sln-file&quot;&gt;理解 Visual Studio 解决方案文件格式（.sln） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/a-list-of-project-type-guids&quot;&gt;解决方案文件 sln 中的项目类型 GUID - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而如果是 nuget 自动识别出来的 MSBuild 版本过旧，则你会同时看到下面的这段提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;NuGet Version: 5.1.0.6013&lt;/p&gt;

  &lt;p&gt;MSBuild auto-detection: using msbuild version ‘15.9.21.664’ from ‘C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin’. Use option -MSBuildVersion to force nuget to use a specific version of MSBuild.&lt;/p&gt;

  &lt;p&gt;Error parsing solution file at C:\walterlv\Walterlv.Demo\Walterlv.Demo.sln: 调用的目标发生了异常。  Error parsing the nested project section in solution file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是解决方法是使 NuGet 能够找到正确的 MSBuild.exe 的版本。&lt;/p&gt;

&lt;p&gt;我在另一篇博客中有写一些决定 MSBuild.exe 版本的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/specify-msbuild-version-for-nuget-command-line&quot;&gt;为 NuGet 指定检测的 MSBuild 路径或版本，解决 MSBuild auto-detection: using msbuild version 自动查找路径不合适的问题 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以通过设置环境变量的方式来解决自动查找版本错误的问题。&lt;/p&gt;

&lt;p&gt;你可以看到本文后面附带了很多的参考资料，但实际上这里的所有资料都没有帮助我解决掉任何问题。这个问题的本质是 nuget 识别到了旧版本的 MSBuild.exe。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/corefx/issues/7040&quot;&gt;Nested project issues · Issue #7040 · dotnet/corefx&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/corefx/pull/7041/files&quot;&gt;Fixed nested project issues in msbuild by svick · Pull Request #7041 · dotnet/corefx&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/36777583/6233938&quot;&gt;MSBuild: Command Line Build error: Solution file error MSB5023: Error parsing the nested project section - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/windows/en-US/30bcd671-58f6-4613-baa0-1ebdb55bd3f3/msbuild-detecting-wrong-version-of-visual-studio?forum=msbuild&quot;&gt;MSBuild detecting wrong version of Visual Studio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/49997388/6233938&quot;&gt;VS2017 MSBuild autodetection takes MSBuild/v14 instead of v15 for WPF project - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/49822757/6233938&quot;&gt;How Can I Tell NuGet What MSBuild Executable to Use? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-restore&quot;&gt;NuGet CLI restore command - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 25 Jul 2019 11:35:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/error-parsing-the-nested-project-section-in-solution-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/error-parsing-the-nested-project-section-in-solution-file.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>为 NuGet 指定检测的 MSBuild 路径或版本，解决 MSBuild auto-detection: using msbuild version 自动查找路径不合适的问题</title>
        <description>&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 命令还原项目的 NuGet 包的时候，NuGet 会尝试自动检测计算机上已经安装的 MSBuild。不过，如果你同时安装了 Visual Studio 2017 和 Visual Studio 2019，那么 NuGet 有可能找到错误版本的 MSBuild。&lt;/p&gt;

&lt;p&gt;本文介绍如何解决自动查找版本错误的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;当我们敲下 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 命令的时候，命令行的第 2 行会输出自动检测到的 MSBuild 版本号，就像下面的输出一样：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;NuGet Version: 5.0.2.5988&lt;br /&gt;
MSBuild auto-detection: using msbuild version ‘15.9.21.664’ from ‘C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin’. Use option -MSBuildVersion to force nuget to use a specific version of MSBuild.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;实际上我计算机上同时安装了 Visual Studio 2017 和 Visual Studio 2019，我有两个不同版本的 MSBuild：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;15.9.21.664
    &lt;ul&gt;
      &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;16.1.76.45076
    &lt;ul&gt;
      &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要让 NuGet 找到正确版本的 MSBuild.exe，我们有三种方法。&lt;/p&gt;

&lt;h2 id=&quot;使用命令行参数解决&quot;&gt;使用命令行参数解决&lt;/h2&gt;

&lt;p&gt;实际上前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 命令的输出中就已经可以看出来其中一个解决方法了，即使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildVersion&lt;/code&gt; 来指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 的版本号。&lt;/p&gt;

&lt;p&gt;虽然命令行输出中推荐使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildVersion&lt;/code&gt; 选项来指定 MSBuild 的版本，但是实际上实现同样功能的有两个不同的选项：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildPath&lt;/code&gt; 自 NuGet 4.0 开始新增的选项，指定 MSBuild 程序的路径。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildVersion&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当同时指定上面两个选项时，&lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildPath&lt;/code&gt; 选项优先级高于 &lt;code class=&quot;highlighter-rouge&quot;&gt;-MSBuildVersion&lt;/code&gt; 选项。&lt;/p&gt;

&lt;p&gt;于是我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 命令改成这样写：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-MSBuildPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出现在会使用期望的 MSBuild 了：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;Using&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;修改环境变量解决&quot;&gt;修改环境变量解决&lt;/h2&gt;

&lt;p&gt;NuGet 的命令行自动查找 MSBuild.exe 时，是通过环境变量中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PATH&lt;/code&gt; 变量来找的。会找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PATH&lt;/code&gt; 中第一个包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild.exe&lt;/code&gt; 文件的路径，将其作为自动查找到的 MSBuild 的路径。&lt;/p&gt;

&lt;p&gt;所以，我们的解决方法是，如果找错了，我们就把期望正确的 MSBuild 所在的路径设置到不期望的 MSBuild 路径的前面。就像下图这样，我们把 2019 版本的 MSBuild 设置到了 2017 版本的前面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-25-18-07-00.png&quot; alt=&quot;设置环境变量&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以下是 NuGet 项目中自动查找 MSBuild.exe 文件的方法，源代码来自 &lt;a href=&quot;https://github.com/NuGet/NuGet.Client/blob/2b45154b8568d6cbf1469f414938f0e3e88e3704/src/NuGet.Clients/NuGet.CommandLine/MsBuildUtility.cs#L986&quot;&gt;https://github.com/NuGet/NuGet.Client/blob/2b45154b8568d6cbf1469f414938f0e3e88e3704/src/NuGet.Clients/NuGet.CommandLine/MsBuildUtility.cs#L986&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMSBuild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exeNames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;msbuild.exe&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RuntimeEnvironmentHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsMono&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;exeNames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;msbuild&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;xbuild&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Try to find msbuild or xbuild in $Path.&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathDirs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PATH&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PathSeparator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringSplitOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveEmptyEntries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathDirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exeName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exeNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathDirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我故意在桌面上放了一个老旧的 MSBuild.exe，然后将此路径设置到环境变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;PATH&lt;/code&gt; 的前面，出现了编译错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-25-18-11-09.png&quot; alt=&quot;编译错误&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-restore&quot;&gt;NuGet CLI restore command - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/49823570/6233938&quot;&gt;How Can I Tell NuGet What MSBuild Executable to Use? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/50014934/6233938&quot;&gt;VS2017 MSBuild autodetection takes MSBuild/v14 instead of v15 for WPF project - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 25 Jul 2019 10:11:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/specify-msbuild-version-for-nuget-command-line.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/specify-msbuild-version-for-nuget-command-line.html</guid>
        
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>解决方案文件 sln 中的项目类型 GUID</title>
        <description>&lt;p&gt;Visual Studio 可以通过得知项目类型快速地为项目显示相应的图标、对应的功能等等。&lt;/p&gt;

&lt;p&gt;本文整理已收集到的一些项目的 GUID，如果你把你的解决方案文件（sln）改坏了，那么可以修复一下。&lt;/p&gt;

&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;8BB2217D-0F2D-49D1-97BC-3654ED321F3B&lt;/code&gt; ASP.NET 5&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;603C0E0B-DB56-11DC-BE95-000D561079B0&lt;/code&gt; ASP.NET MVC 1&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F85E285D-A4E0-4152-9332-AB1D724D3325&lt;/code&gt; ASP.NET MVC 2&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;E53F8FEA-EAE0-44A6-8774-FFD645390401&lt;/code&gt; ASP.NET MVC 3&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;E3E379DF-F4C6-4180-9B81-6769533ABE47&lt;/code&gt; ASP.NET MVC 4&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;349C5851-65DF-11DA-9384-00065B846F21&lt;/code&gt; ASP.NET MVC 5 / Web Application&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FAE04EC0-301F-11D3-BF4B-00C04F79EFBC&lt;/code&gt; C#&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;9A19103F-16F7-4668-BE54-9A1E7A4F7556&lt;/code&gt; C# (SDK 风格的项目)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942&lt;/code&gt; C++&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124&lt;/code&gt; Database&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;4F174C21-8C12-11D0-8340-0000F80270F8&lt;/code&gt; Database (other project types)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;3EA9E505-35AC-4774-B492-AD1749C4943A&lt;/code&gt; Deployment Cab&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;06A35CCD-C46D-44D5-987B-CF40FF872267&lt;/code&gt; Deployment Merge Module&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;978C614F-708E-4E1A-B201-565925725DBA&lt;/code&gt; Deployment Setup&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AB322303-2255-48EF-A496-5904EB18DA55&lt;/code&gt; Deployment Smart Device Cab&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F135691A-BF7E-435D-8960-F99683D2D49C&lt;/code&gt; Distributed System&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BF6F8E12-879D-49E7-ADF0-5503146B24B8&lt;/code&gt; Dynamics 2012 AX C# in AOT&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;82B43B9B-A64C-4715-B499-D71E9CA2BD60&lt;/code&gt; Extensibility&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F2A71F9B-5D33-465A-A702-920D77279786&lt;/code&gt; F#&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705&lt;/code&gt; F# (SDK 风格的项目)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;95DFC527-4DC1-495E-97D7-E94EE1F7140D&lt;/code&gt; IL project&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;E6FDF86B-F3D1-11D4-8576-0002A516ECE8&lt;/code&gt; J#&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;262852C6-CD72-467D-83FE-5EEB1973A190&lt;/code&gt; JScript&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;20D4826A-C6FA-45DB-90F4-C717570B9F32&lt;/code&gt; Legacy (2003) Smart Device (C#)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8&lt;/code&gt; Legacy (2003) Smart Device (VB.NET)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;581633EB-B896-402F-8E60-36F3DA191C85&lt;/code&gt; LightSwitch Project&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;8BB0C5E8-0616-4F60-8E55-A43933E57E9C&lt;/code&gt; LightSwitch&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;b69e3092-b931-443c-abe7-7e7b65f2a37f&lt;/code&gt; Micro Framework&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C1CDDADD-2546-481F-9697-4EA41081F2FC&lt;/code&gt; Office/SharePoint App&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;786C830F-07A1-408B-BD7F-6EE04809D6DB&lt;/code&gt; Portable Class Library&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;66A26720-8FB5-11D2-AA7E-00C04F688DDE&lt;/code&gt; Project Folders&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D954291E-2A0B-460D-934E-DC6B0785DB48&lt;/code&gt; Shared Project&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;593B0543-81F6-4436-BA1E-4747859CAAE2&lt;/code&gt; SharePoint (C#)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EC05E597-79D4-47f3-ADA0-324C4F7C7484&lt;/code&gt; SharePoint (VB.NET)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F8810EC1-6754-47FC-A15F-DFABD2E3FA90&lt;/code&gt; SharePoint Workflow&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A1591282-1198-4647-A2B1-27E5FF5F6F3B&lt;/code&gt; Silverlight&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;4D628B5B-2FBC-4AA6-8C16-197242AEB884&lt;/code&gt; Smart Device (C#)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;68B1623D-7FB9-47D8-8664-7ECEA3297D4F&lt;/code&gt; Smart Device (VB.NET)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;2150E333-8FDC-42A3-9474-1A3956D46DE8&lt;/code&gt; 解决方案文件夹&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;3AC096D0-A1C2-E12C-1390-A8335801FDAB&lt;/code&gt; Test&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A5A43C5B-DE2A-4C0C-9213-0A381AF9435A&lt;/code&gt; Universal Windows Class Library&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F184B08F-C81C-45F6-A57F-5ABD9991F28F&lt;/code&gt; VB.NET&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;778DAE3C-4631-46EA-AA77-85C1314464D9&lt;/code&gt; VB.NET (forces use of SDK project system)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C252FEB5-A946-4202-B1D4-9916A0590387&lt;/code&gt; Visual Database Tools&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;54435603-DBB4-11D2-8724-00A0C9A8B90C&lt;/code&gt; Visual Studio 2015 Installer Project Extension&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A860303F-1F3F-4691-B57E-529FC101A107&lt;/code&gt; Visual Studio Tools for Applications (VSTA)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BAA0C2D2-18E2-41B9-852F-F413020CAA33&lt;/code&gt; Visual Studio Tools for Office (VSTO)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;E24C65DC-7377-472B-9ABA-BC803B73C61A&lt;/code&gt; Web Site&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;3D9AD99F-2412-4246-B90B-4EAA41C64699&lt;/code&gt; Windows Communication Foundation (WCF)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;76F1466A-8B6D-4E39-A767-685A06062A39&lt;/code&gt; Windows Phone 8/8.1 Blank/Hub/Webview App&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C089C8C0-30E0-4E22-80C0-CE093F111A43&lt;/code&gt; Windows Phone 8/8.1 App (C#)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DB03555F-0C8B-43BE-9FF9-57896B3C5E56&lt;/code&gt; Windows Phone 8/8.1 App (VB.NET)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;60DC8134-EBA5-43B8-BCC9-BB4BC16C2548&lt;/code&gt; Windows Presentation Foundation (WPF)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BC8A1FFA-BEE3-4634-8014-F334798102B3&lt;/code&gt; Windows Store (Metro) Apps &amp;amp; Components&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D954291E-2A0B-460D-934E-DC6B0785DB48&lt;/code&gt; Windows Store App Universal&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;14822709-B5A1-4724-98CA-57A101D1B079&lt;/code&gt; Workflow (C#)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8&lt;/code&gt; Workflow (VB.NET)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;32F31D43-81CC-4C15-9DE6-3FC5453562B6&lt;/code&gt; Workflow Foundation&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EFBA0AD7-5A72-4C68-AF49-83D382785DCF&lt;/code&gt; Xamarin.Android / Mono for Android&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;6BC8ED88-2882-458C-8E55-DFD12B67127B&lt;/code&gt; Xamarin.iOS / MonoTouch&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F5B4F3BC-B597-4E2B-B552-EF5D8A32436F&lt;/code&gt; MonoTouch Binding&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;6D335F3A-9D43-41b4-9D22-F6F17C4BE596&lt;/code&gt; XNA (Windows)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2&lt;/code&gt; XNA (XBox)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D399B71A-8929-442a-A9AC-8BEC78BB2433&lt;/code&gt; XNA (Zune)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/53485177/6233938&quot;&gt;csproj - Visual Studio project type guids - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mztools.com/articles/2008/mz2008017.aspx&quot;&gt;INFO: List of known project type Guids&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs&quot;&gt;List of Visual Studio Project Type GUIDs - CodeProject&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 24 Jul 2019 03:06:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/a-list-of-project-type-guids.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/a-list-of-project-type-guids.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>使用 Roslyn 分析代码注释，给 TODO 类型的注释添加负责人、截止日期和 issue 链接跟踪</title>
        <description>&lt;p&gt;如果某天改了一点代码但是没有完成，我们可能会在注释里面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;// TODO&lt;/code&gt;。如果某个版本为了控制影响范围临时使用不太合适的方法解了 Bug，我们可能也会在注释里面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;// TODO&lt;/code&gt;。但是，对于团队项目来说，一个人写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 可能过了一段时间就淹没在大量的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 堆里面了。如果能够强制要求所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 被跟踪，那么代码里面就比较容易能够控制住 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 的影响了。&lt;/p&gt;

&lt;p&gt;本文将基于 Roslyn 开发代码分析器，要求所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 注释具有可被跟踪的负责人等信息。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;预备知识&quot;&gt;预备知识&lt;/h2&gt;

&lt;p&gt;如果你对基于 Roslyn 编写分析器和代码修改器不了解，建议先阅读我的一篇入门教程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/develop-a-code-analyzer-for-both-nuget-and-visual-studio-extension&quot;&gt;基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;分析器&quot;&gt;分析器&lt;/h2&gt;

&lt;p&gt;我们先准备一些公共的信息：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiagnosticIds&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 标记了待办事项的代码必须被追踪。WAL 是我名字（walterlv）的前三个字母。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TodoMustBeTracked&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;WAL302&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在后面的代码分析器和修改器中，我们将都使用此公共的字符串常量来作为诊断 Id。&lt;/p&gt;

&lt;p&gt;我们先添加分析器（&lt;code class=&quot;highlighter-rouge&quot;&gt;TodoMustBeTrackedAnalyzer&lt;/code&gt;）最基础的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TodoMustBeTrackedAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DiagnosticIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TodoMustBeTracked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;任务必须被追踪&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;&quot;未完成的任务缺少负责人和完成截止日期：{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Maintainability&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DiagnosticSeverity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isEnabledByDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;未完成的任务必须有对应的负责人和截止日期（// TODO @lvyi 2019-08-01），最好有任务追踪系统（如 JIRA）跟踪。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSyntaxTreeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalyzeSingleLineComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeSingleLineComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxTreeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这里将是我们分析器的主要代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来我们则是要完善语法分析的部分，我们需要找到单行注释和多行注释。&lt;/p&gt;

&lt;p&gt;注释在语法节点中不影响代码含义，这些不影响代码含义的语法部件被称作 &lt;code class=&quot;highlighter-rouge&quot;&gt;Trivia&lt;/code&gt;（闲杂部件）。这跟我前面入门教程部分说的语法节点不同，其 API 会少一些，但也更加简单。&lt;/p&gt;

&lt;p&gt;我们从语法树的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DescendantTrivia&lt;/code&gt; 方法中可以拿到文档中的所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Trivia&lt;/code&gt; 然后过滤掉获得其中的注释部分。&lt;/p&gt;

&lt;p&gt;比如，我们要分析下面的这个注释：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// TODO 林德熙在这个版本写的逗比代码，下个版本要改掉。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在语法节点中判断注释的袋子性，然后使用正则表达式匹配 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt;、负责人以及截止日期即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TodoRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;//\s*todo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssigneeRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;@\w+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;[\d]{4}\s?[年\-\.]\s?[01]?[\d]\s?[月\-\.]\s?[0123]?[\d]\s?日?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeSingleLineComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxTreeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DescendantTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SingleLineCommentTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultiLineCommentTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;todoMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TodoRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;todoMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assigneeMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssigneeRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assigneeMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReportDiagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将上面的类组装起来运行 Visual Studio 调试即可看到效果。没有负责人和截止日期的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO&lt;/code&gt; 注释将报告编译错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-20-22-04-53.png&quot; alt=&quot;注释上的编译错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TodoMustBeTrackedAnalyzer&lt;/code&gt; 类型的完整代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Immutable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp.Syntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Analyzers.Maintainability&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TodoMustBeTrackedAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;任务必须被追踪&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MessageFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;未完成的任务缺少负责人和完成截止日期：{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;未完成的任务必须有对应的负责人和截止日期（// TODO @lvyi 2019-08-01），最好有任务追踪系统（如 JIRA）跟踪。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TodoRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;//\s*todo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssigneeRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;@\w+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;[\d]{4}\s?[年\-\.]\s?[01]?[\d]\s?[月\-\.]\s?[0123]?[\d]\s?日?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DiagnosticIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TodoMustBeTracked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MessageFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Categories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Maintainability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DiagnosticSeverity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isEnabledByDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnableConcurrentExecution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureGeneratedCodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeAnalysisFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Analyze&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedCodeAnalysisFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReportDiagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSyntaxTreeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalyzeSingleLineComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeSingleLineComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxTreeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DescendantTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SingleLineCommentTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultiLineCommentTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;todoMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TodoRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;todoMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assigneeMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssigneeRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assigneeMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReportDiagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;代码修改器&quot;&gt;代码修改器&lt;/h2&gt;

&lt;p&gt;只是报错的话，开发者看到错误可能会一脸懵逼，因为从未见过注释还会报告编译错误的，不知道怎么改。&lt;/p&gt;

&lt;p&gt;于是我们需要编写一个代码修改器以便自动完成注释的修改，添加负责人和截止日期。我这里代码修改器修改后的结果就像下面这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-20-22-05-14.png&quot; alt=&quot;TODO 注释的代码修改器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;生成一个新的注释字符串然后替换即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Immutable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CodeActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CodeFixes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Analyzers.Maintainability&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExportCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TodoMustBeTrackedCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TodoMustBeTrackedCodeFixProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CodeFixProvider&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;添加任务负责人 / 完成日期 / JIRA Id 追踪&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssigneeRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;@\w+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRegex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;[\d]{4}\s?[年\-\.]\s?[01]?[\d]\s?[月\-\.]\s?[0123]?[\d]\s?日?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegexOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Compiled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixableDiagnosticIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TodoMustBeTracked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixAllProvider&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFixAllProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WellKnownFixAllProviders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchFixer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterCodeFixesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeFixContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterCodeFix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FormatTrackableTodoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TodoMustBeTrackedCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompletedTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FormatTrackableTodoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Diagnostic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxRootAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTrivia&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;todo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;// TODO @&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yyyy&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;年&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;月&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;日&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTrivia&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParseTrailingTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReplaceTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithSyntaxRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 22 Jul 2019 03:43:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/comment-analyzer-and-code-fix-using-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/comment-analyzer-and-code-fix-using-roslyn.html</guid>
        
        
        <category>roslyn</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景</title>
        <description>&lt;p&gt;有小伙伴看到我有时写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if&lt;/code&gt; 有时写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Conditional]&lt;/code&gt; 问我两个不是一样的吗，何必多此一举。然而实际上两者的编译处理是不同的，因此也有不同的应用场景。&lt;/p&gt;

&lt;p&gt;于是我写到这篇文章当中。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;条件编译符号和预处理符号&quot;&gt;条件编译符号和预处理符号&lt;/h2&gt;

&lt;p&gt;我们有时会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Conditional(&quot;DEBUG&quot;)]&lt;/code&gt; 来让我们的代码仅在特定的条件下编译。&lt;/p&gt;

&lt;p&gt;而这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DEBUG&lt;/code&gt; 是什么呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在我们编写的 C# 代码中，这个叫做 “条件编译符号”（Conditional compilation symbols）&lt;/li&gt;
  &lt;li&gt;在项目的构建过程中，这个叫做 “定义常量”（Define constants）&lt;/li&gt;
  &lt;li&gt;而在将 C# 代码编译到 dll 的编译环节，这个叫做 “预处理符号”（Preprocessor symbols）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文要讨论的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Conditional&lt;/code&gt; 的使用，这是在 C# 代码中的使用场景，因此，本文后面都将其称之为 “条件编译符号”。&lt;/p&gt;

&lt;h2 id=&quot;区别&quot;&gt;区别&lt;/h2&gt;

&lt;h3 id=&quot;if&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#if&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#if DEBUG
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎来 blog.walterlv.com 来做客呀！&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;#endif&lt;/code&gt; 之间的代码仅在 DEBUG 下会编译，在其他配置下是不会编译的。&lt;/p&gt;

&lt;h3 id=&quot;conditional&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Conditional&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Conditional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DEBUG&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎来 blog.walterlv.com 来做客呀！&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而这段代码，是会被编译到目标程序集中的。它影响的，是调用这个方法的代码。调用这个方法的代码，仅在 DEBUG 下会编译，在其他配置下是不会编译的。&lt;/p&gt;

&lt;h2 id=&quot;场景&quot;&gt;场景&lt;/h2&gt;

&lt;p&gt;因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;#endif&lt;/code&gt; 仅仅影响包含在其内的代码块，因此其仅仅影响写的这点代码所在的项目（或者说程序集）。于是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if&lt;/code&gt; 只会影响实现代码。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Conditional(&quot;DEBUG&quot;)]&lt;/code&gt; 影响的是调用它的代码，因此可以设计作为 API 使用——让目标项目（或者程序集）仅在目标项目特定的配置下才会编译。&lt;/p&gt;
</description>
        <pubDate>Sat, 20 Jul 2019 07:41:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/preprocessor-symbols-if-vs-conditional.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/preprocessor-symbols-if-vs-conditional.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何为你的 Windows 应用程序关联 URL 协议，以便在浏览器中也能打开你的应用</title>
        <description>&lt;p&gt;移动程序关联 URL 是常态，桌面应用程序其实也早就支持关联 URL 以便在浏览器中打开。当我们的程序关联了一个 URL 协议之后，开发的网站上就可以通过这个 URL 与程序进行互操作，这很互联网。&lt;/p&gt;

&lt;p&gt;对于 Windows 桌面应用来说，关联一个 URL 协议是通过修改注册表来实现的。本文介绍如何为你的应用关联一个 URL 协议。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;url-协议&quot;&gt;URL 协议&lt;/h2&gt;

&lt;p&gt;一个常用的 URL 协议是这样子的：&lt;a href=&quot;https://walterlv.com&quot;&gt;https://walterlv.com&lt;/a&gt;。前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt; 就是协议名称，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;https://&lt;/code&gt; 放在一起就是在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt; 协议。&lt;/p&gt;

&lt;p&gt;本文我们将定义一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 协议，然后关联到我们本地安装的一个桌面应用程序上，然后使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv://open?id=1&lt;/code&gt; 来打开一个 id 为 1 的逗比。&lt;/p&gt;

&lt;h2 id=&quot;注册一个-url-协议&quot;&gt;注册一个 URL 协议&lt;/h2&gt;

&lt;p&gt;要在 Windows 系统上注册一个 URL 协议，你只需要两个步骤：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;好好想一个协议名称&lt;/li&gt;
  &lt;li&gt;在注册表中添加协议关联&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;好好想一个协议名称&quot;&gt;好好想一个协议名称&lt;/h3&gt;

&lt;p&gt;就知道你想不出来名字，于是可以使用命名生成工具：&lt;a href=&quot;ms-windows-store://pdp/?productid=9P8LNZRNJX85&quot;&gt;Whitman&lt;/a&gt;，其原理可阅读 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;然后本文使用协议名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;在注册表中添加协议关联&quot;&gt;在注册表中添加协议关联&lt;/h3&gt;

&lt;p&gt;你需要在注册表的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 添加一些子键：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_CURRENT_USER\Software\Classes
    walterlv
        (Default) = 吕毅的特殊链接
        URL Protocol = WalterlvProtocol
        Shell
            Open
                Command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\Walterlv.Windows.Association.exe&quot; &quot;%1&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Classes&lt;/code&gt; 中的那个根键 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 就是我们的协议名称，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv://&lt;/code&gt; 的那个前缀。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 根键 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(Default)&lt;/code&gt; 属性给出的是链接的名称；如果后面没有设置打开方式（也就是那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Shell\Open\Command&lt;/code&gt;）的话，那么在 Chrome 里打开就会显示为那个名称（如下图）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-03-16-19-28.png&quot; alt=&quot;默认的协议名称&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;URL Protocol&lt;/code&gt; 这个注册表项是必须存在的，但里面的值是什么其实无所谓。这只是表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 是一个协议。&lt;/p&gt;

&lt;p&gt;接下来 &lt;code class=&quot;highlighter-rouge&quot;&gt;Shell\Open\Command&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(Default)&lt;/code&gt; 值设置为一个打开此协议用的命令行。其中路径后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;%1&quot;&lt;/code&gt; 是文件资源管理器传入的参数，其实就是文件的完整路径。我们加上了引号是避免解析命令行的时候把包含空格的路径拆成了多个参数。&lt;/p&gt;

&lt;p&gt;在正确填写了注册表的以上内容之后，在 Chrome 里打开此链接将看到以下 URL 打开提示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-03-16-24-31.png&quot; alt=&quot;带有打开命令的协议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;关于注册表路径的说明&lt;/strong&gt;：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE&lt;/code&gt; 主键是此计算机上的所有用户共享的注册表键值，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER&lt;/code&gt; 是当前用户使用的注册表键值。而我们在注册表的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 中也可以看到跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 中一样的文件关联项，是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 合并之后的一个视图，其中用户键值会覆盖此计算机上的相同键值。&lt;/p&gt;

&lt;p&gt;也就是说，如果你试图修改文件关联，那么需要去 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 中，但如果只是去查看文件关联的情况，则只需要去 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;写入计算机范围内的注册表项需要管理员权限，而写入用户范围内的注册表项不需要管理员权限；你可以酌情选用。&lt;/p&gt;

&lt;h3 id=&quot;额外说明&quot;&gt;额外说明&lt;/h3&gt;

&lt;p&gt;感谢 &lt;a href=&quot;https://me.csdn.net/xnyqh&quot;&gt;人猿&lt;/a&gt; 提供的补充信息：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;假如初次点击不打开，并且勾选了始终，那么以后这个弹框就没有了，而程序也不会打开，需要做下配置的修改 谷歌浏览器：C:\Users(你的用户名)\AppData\Local\Google\Chrome\User Data\Default\Preferences 火狐浏览器：先关闭浏览器C:\Users(你的用户名)\AppData\Roaming\Mozilla\Firefox\Profiles\4uasyvvi.default 找到handlers.json&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Sat, 20 Jul 2019 01:15:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-uri-scheme-association.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-uri-scheme-association.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>软件界面中一些易混淆/易用错的界面文案，以及一些约定俗成的文案约定</title>
        <description>&lt;p&gt;经常有小伙伴跟我撕到底一些常用同音的词语应该使用哪个的问题。于是我将一些常用的软件界面中用错的文案整理出来，为自己和其他开发者提供我 &lt;strong&gt;已经整理的结论&lt;/strong&gt; 和 &lt;strong&gt;可以溯源的资料&lt;/strong&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;词语&quot;&gt;词语&lt;/h2&gt;

&lt;p&gt;下面列举出来的一些词语，有的我写的是 “推荐”，指两者都是正确的，但更应该使用 “推荐” 中的词语；而有的我写的是 “正确”，指只有这一个才是正确的，而其他写法是错误的。&lt;/p&gt;

&lt;p&gt;无论哪一种，都说明了理由和可溯源的资料。&lt;/p&gt;

&lt;h3 id=&quot;撤销--撤消&quot;&gt;撤销 / 撤消&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;推荐：在软件界面中推荐使用 “撤销”。应该逐渐淘汰“撤消”的使用。&lt;/li&gt;
  &lt;li&gt;实际：国产软件主要使用“撤销”，而国外软件的中文版本两者都有使用。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;看《现代汉语词典》：&lt;/p&gt;

&lt;p&gt;第五、六、七版：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;【撤销】chèxiāo  [动] 取消：～处分｜～职务。也作撤消。
【撤消】chèxiāo  同“撤销”。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;第三版：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;【撤销】chèxiāo  [动] 撤除；取消 |&amp;gt; ～原判决 | ～多余的机构。☞ 不宜写作“撤消”。
【撤消】chèxiāo  现在一般写作“撤销”。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可见，“撤消”已经被淘汰，现全部应该使用“撤销”。&lt;/p&gt;

&lt;p&gt;那么实际中大家是如何使用的呢？&lt;/p&gt;

&lt;p&gt;Windows 系统和 Office 套件使用的是“撤消”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-12-13-25.png&quot; alt=&quot;Windows 文件资源管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-12-17-27.png&quot; alt=&quot;Office 套件&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;撤销恢复--撤销重做撤消恢复--撤消重做&quot;&gt;撤销恢复 / 撤销重做，撤消恢复 / 撤消重做&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;正确：恢复。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;撤销：Undo。恢复：Redo。重做：Repeat。&lt;/p&gt;

&lt;p&gt;有些软件会出现此错误，估计跟 Office 的使用有关。&lt;/p&gt;

&lt;p&gt;在正常情况下，Office 的左上角有一对按钮：“撤消” 和 “重做”。但是，“重做” 的意思真的是 “重复上一步操作”。当你点了 “撤消” 之后，这个 “重做” 按钮会消失，变成 “恢复” 按钮，意思是将刚刚 “撤消” 的操作 “恢复” 回来。&lt;/p&gt;

&lt;p&gt;因此，如果只是在 Office 软件里看了一眼就把文案抄过来了，那就会出现 “撤消重做” 这样的误用；实际上应该是 “撤销恢复”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-12-17-27.png&quot; alt=&quot;Office 套件&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/JasonStein/Notepads/pull/32#discussion_r300894925&quot;&gt;Added zh-CN translation by lindexi · Pull Request #32 · JasonStein/Notepads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;账号--帐号账户--帐户&quot;&gt;账号 / 帐号，账户 / 帐户&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;推荐：在软件 zhànghào / zhànghù 界面中推荐使用 “账号” 和 “账户”。&lt;/li&gt;
  &lt;li&gt;实际：各大软件平分秋色，都有使用。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 &lt;a href=&quot;https://zh.wikisource.org/zh-hans/%E7%AC%AC%E4%B8%80%E6%89%B9%E5%BC%82%E5%BD%A2%E8%AF%8D%E6%95%B4%E7%90%86%E8%A1%A8&quot;&gt;第一批异形词整理表&lt;/a&gt; 中对于 “账” 和 “帐” 的用法有一项相关的说明，明确 “账本”（zhàngběn）一词是普通话书面语中推荐的使用词形，而 “帐本” 是 “账本” 异形词。&lt;/p&gt;

&lt;p&gt;其对于 “账” 和 “帐” 的解释如下：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“账”是“帐”的分化字。古人常把账目记于布帛上悬挂起来以利保存，故称日用的账目为“帐”。后来为了与帷帐分开，另造形声字“账”，表示与钱财有关。“账”“帐”并存并用后，形成了几十组异形词。《简化字总表》、《现代汉语通用字表》中“账”“帐”均收，可见主张分化。二字分工如下：“账”用于货币和货物出入的记载、债务等，如“账本、报账、借账、还账”等；“帐”专表用布、纱、绸子等制成的遮蔽物，如“蚊帐、帐篷、青纱帐（比喻用法）”等。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;从主张分化的目的来看，其更推荐在表示“货币和货物出入的记载、债务”时使用“账”，而在表示“布、纱、绸子等制成的遮蔽物”时使用“帐”。那么软件界面中应该使用哪一个呢？&lt;/p&gt;

&lt;p&gt;对于“支付宝”/“京东”/“淘宝”/“微信钱包”/各类银行这些一看就跟钱相关的应用里面，很明显推荐使用“账户”。另外一些如论坛 zhànghào，QQ zhànghào 等没有明前与钱相关的应用，其通常也包含一些虚拟的服务行为记录、以及与其他用户相关的虚拟交易方式（例如论坛币、Q 币），因此也推荐使用“账户”。&lt;/p&gt;

&lt;p&gt;然而还有一些与这些虚拟交易也没有关系的，非营利组织的或者完全个人的 zhànghào，应该使用什么呢？这些 zhànghù 通常只做一些密码记录、行为记录、用户个人设置个人偏好存储等。从含义上讲，这些信息与“账”描述中的“货物出入的记载”这一句是相关的，而与“帐”中的“布、纱、绸子等制成的遮蔽物”不相关。因此，即便是这些与钱不直接相关的用户 zhànghù 或者 zhànghào 也更加推荐使用 “账号” 和 “账户”。&lt;/p&gt;

&lt;p&gt;那么实际中大家是如何使用的呢？&lt;/p&gt;

&lt;p&gt;在我们刚刚参考的维基文库中，其使用的就是 “账号”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-11-40-43.png&quot; alt=&quot;维基文库&quot; /&gt;&lt;/p&gt;

&lt;p&gt;京东/1号店/支付宝的登录页面使用了 “账号”（淘宝使用了“会员名”来规避了这种争议词的使用）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-11-44-19.png&quot; alt=&quot;京东&quot; /&gt;&lt;/p&gt;

&lt;p&gt;淘宝使用了“会员名”来规避了这种争议词的使用。&lt;/p&gt;

&lt;p&gt;QQ/微信/网易中使用的是 “帐号”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-11-42-51.png&quot; alt=&quot;QQ 登录页面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Windows 系统采用了 “帐户” 一词。不过其中文版对此异形词做了很友好的适配，无论你输入哪一个词，最终都可以搜到你想要的 zhànghù：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-10-12-26.png&quot; alt=&quot;Windows 系统设置中的帐户&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你以为微软统一使用 “帐户” 吗？实际上可以看看下面这个页面，两个词都有使用。微软一定很纠结。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoftstore.com.cn/support-and-help/account-faq/zhucemicrosoftzhanghu&quot;&gt;怎么注册Microsoft账户 - 常见问题 - 微软官方商城&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-13-47-11.png&quot; alt=&quot;纠结的微软&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;登录--登陆&quot;&gt;登录 / 登陆&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;正确：“登录” 才是正确用法。“登陆”根本就不是计算机术语。&lt;/li&gt;
  &lt;li&gt;实际：主流软、大公司基本都正确使用了 “登录”，但其他网站就不好说了各种乱用。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;标点符号&quot;&gt;标点符号&lt;/h2&gt;

&lt;h3 id=&quot;句号&quot;&gt;句号&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;推荐：句子的结尾必须有句号或者可以承担句号职责的标点；而短语后面则不应该加句号或同类标点。&lt;/li&gt;
  &lt;li&gt;实际：很多不成熟的软件会在句子结尾不带任何句号或同类标点。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为什么连句号也要拿出来说呢？&lt;/p&gt;

&lt;h3 id=&quot;省略号&quot;&gt;省略号&lt;/h3&gt;

&lt;p&gt;从早期的界面设计中一直延续下来一个约定：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果某个按钮有后续操作，那么这个按钮的名称后面需要带上省略号 “…”。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;del&gt;注意，这是半个省略号 “…”，而不是三个点 “…”。无论中文还是英文都如此。&lt;/del&gt; 正在搜寻资料确认到底是什么。&lt;/p&gt;

&lt;p&gt;后续操作指的是“需要提供额外的信息”。例如“保存”直接存成文件，而“另存为”需要提供一个新的文件名。因此“保存”没有省略号而“另存为”则有省略号。&lt;/p&gt;

&lt;p&gt;这个约定在微软的 Windows 系统中和苹果的 macOS 系统中原本一直都有执行下去，就像下面这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-13-51-04.png&quot; alt=&quot;Windows 系统&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-13-56-38.png&quot; alt=&quot;Mac 系统&quot; /&gt;&lt;/p&gt;

&lt;p&gt;直到后来发现，如果继续执行这项约定，那么整个界面中将充斥着省略号，非常影响美观。&lt;/p&gt;

&lt;p&gt;于是后来就只在菜单中保留这项约定，其他常显界面中就去掉了省略号：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-14-02-30.png&quot; alt=&quot;Windows 文件资源管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-14-00-27.png&quot; alt=&quot;Windows 设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-08-14-04-13.png&quot; alt=&quot;Visual Studio 中的菜单项&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;额外说明&quot;&gt;额外说明&lt;/h2&gt;

&lt;p&gt;可能需要解释一下异形词，来自维基文库：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;异形词（variant forms of the same word）&lt;/p&gt;

  &lt;p&gt;普通话书面语中并存并用的同音（本规范中指声、韵、调完全相同）、同义（本规范中指理性意义、色彩意义和语法意义完全相同）而书写形式不同的词语。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而异体字：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;异体字（variant forms of a Chinese character）&lt;/p&gt;

  &lt;p&gt;与规定的正体字同音、同义而写法不同的字。本规范中专指被《第一批异体字整理表》淘汰的异体字。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;对于异形词，其不同的写法需要用在不同的场景中；对于异体字，则需要逐渐淘汰使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikisource.org/zh-hans/%E7%AC%AC%E4%B8%80%E6%89%B9%E5%BC%82%E5%BD%A2%E8%AF%8D%E6%95%B4%E7%90%86%E8%A1%A8&quot;&gt;第一批异形词整理表 - 维基文库，自由的图书馆&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikisource.org/wiki/%E7%AC%AC%E4%B8%80%E6%89%B9%E5%BC%82%E4%BD%93%E5%AD%97%E6%95%B4%E7%90%86%E8%A1%A8&quot;&gt;第一批异体字整理表 - 维基文库，自由的图书馆&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[【撤销】【撤消】&lt;/td&gt;
          &lt;td&gt;× 【C】√ - 校对标准：寻找权威依据 - Powered by phpwind](http://www.jiaodui.com/bbs/read.php?tid=9865)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 13 Jul 2019 01:49:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/some-confusing-ui-text.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/some-confusing-ui-text.html</guid>
        
        
        <category>windows</category>
        
        <category>ui</category>
        
        <category>ux</category>
        
      </item>
    
      <item>
        <title>The VisualBrush of WPF only refresh the visual but not the layout</title>
        <description>&lt;p&gt;Now we’ll talk about a behavior of WPF &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt;. Maybe it is a bug, but let’s view some details and determine whether it is or not.&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-reproduction-code&quot;&gt;The reproduction code&lt;/h2&gt;

&lt;p&gt;Let’s make a XAML layout to reproduce such an issue.&lt;/p&gt;

&lt;p&gt;We have a large &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; which contains an inner &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; and an inner &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; contains a &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; which is as large as the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; itself and a &lt;code class=&quot;highlighter-rouge&quot;&gt;TextBlock&lt;/code&gt; which presents some contents. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; only shows its background which a &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; of the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-20-12-56.png&quot; alt=&quot;The layout that can reproduce this issue&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is the whole XAML file:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv 的 WindowChrome 示例窗口&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;WindowStartupLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CenterScreen&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VisualSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VisibleOr&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LightCoral&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visibility=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visible&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;24&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I'm walterlv, &quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I'm reproducing this Visual bug.&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border.Background&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visual=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Source={x:Reference VisualSource}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border.Background&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the code-behind. Notice that it changes the visibility of the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; every 1 second.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;VisibleOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;VisibleOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;to-verify-the-issue&quot;&gt;To verify the issue&lt;/h2&gt;

&lt;p&gt;We know that the &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; shows and stretch the whole &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; so we can predicate only two results:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; is visible with &lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; property &lt;code class=&quot;highlighter-rouge&quot;&gt;Visible&lt;/code&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; background which contains the &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; will be exactly the same with the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; is invisible with &lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; property &lt;code class=&quot;highlighter-rouge&quot;&gt;Collapsed&lt;/code&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; background which contains the &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; will stretch the whole content to the &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; without any area of the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it is the real result?&lt;/p&gt;

&lt;p&gt;The animation picture below shows the result when the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; is visible as the startup:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-visual-layout-not-refresh-2.gif&quot; alt=&quot;Visible at the beginning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The animation picture below shows the result when the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; is invisible as the startup:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-visual-layout-not-refresh.gif&quot; alt=&quot;Invisible at the beginning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Did you notice that?&lt;/p&gt;

&lt;p&gt;Only at the very beginning when the program runs it behaves the same as we predicted. But when we change the visibility of the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; the layout never changes.&lt;/p&gt;

&lt;h2 id=&quot;the-issue&quot;&gt;The issue?&lt;/h2&gt;

&lt;p&gt;I’ve fired this issue into GitHub and this is the link:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/issues/1241&quot;&gt;The VisualBrush only refresh the visual but not the layout when the Visual visibility changes · Issue #1241 · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Jul 2019 12:53:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/visual-brush-refresh-views-only-but-not-layout-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/visual-brush-refresh-views-only-but-not-layout-en.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 的 VisualBrush 只刷新显示的视觉效果，不刷新布局范围</title>
        <description>&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 可以帮助我们在一个控件中显示另一个控件的外观。这是非常妙的功能。&lt;/p&gt;

&lt;p&gt;但是本文需要说其中的一个 Bug —— 如果使用 VisualBrush 显示另一个控件的外观，那么只会在其显示效果有改变的时候刷新，而不会在目标布局改变的时候刷新布局。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;用于复现问题的代码&quot;&gt;用于复现问题的代码&lt;/h2&gt;

&lt;p&gt;我们现在做一个可以用于验证此问题的布局。&lt;/p&gt;

&lt;p&gt;在一个大的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 容器中有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 和一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 将放一个大面积的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 和一个表示内容的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextBlock&lt;/code&gt;；而那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 将完全以 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 的形式呈现，呈现的内容是此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 中的全部内容。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-20-12-32.png&quot; alt=&quot;可以用于验证此问题的布局&quot; /&gt;&lt;/p&gt;

&lt;p&gt;它的完整 XAML 代码如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv 的 WindowChrome 示例窗口&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;WindowStartupLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CenterScreen&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VisualSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VisibleOr&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LightCoral&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visibility=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visible&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;24&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I'm walterlv, &quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I'm reproducing this Visual bug.&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border.Background&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visual=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Source={x:Reference VisualSource}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border.Background&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其后台 C# 代码如下，包含每隔 1 秒钟切换 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 可见性的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;VisibleOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;VisibleOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;验证问题&quot;&gt;验证问题&lt;/h2&gt;

&lt;p&gt;我们知道，&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 在默认情况下会将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 中的全部内容拉伸到控件中显示，于是可以预估出两个可能的结果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 可见（&lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visible&lt;/code&gt;），那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 中以 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 显示的内容将完全和下面重叠（因为大小相同，拉伸后正好重叠）。&lt;/li&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 不可见（&lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Collapsed&lt;/code&gt;），那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 中以 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 显示的内容将仅有文字且拉伸到整个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 范围。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而实际运行真的是这样子吗？&lt;/p&gt;

&lt;p&gt;下面的动图是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 初始状态可见时，窗口运行后的结果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-visual-layout-not-refresh-2.gif&quot; alt=&quot;初始状态可见&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面的动图是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 初始状态不可见时，窗口运行后的结果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-12-visual-layout-not-refresh.gif&quot; alt=&quot;初始状态不可见&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意到了吗？&lt;/p&gt;

&lt;p&gt;只有初始状态才能正确反应我们之前预估出的结果，而无论后面怎么再改变可见性，布局都不会再刷新了。只是——后面 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 的内容始终重叠。这意味着 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualBrush&lt;/code&gt; 中目标 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 的范围增大之后不会再缩小了。&lt;/p&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题？&lt;/h2&gt;

&lt;p&gt;这是问题吗？&lt;/p&gt;

&lt;p&gt;于是在以下 issue 中跟进此问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;visualbrush-的其他-bug&quot;&gt;VisualBrush 的其他 Bug&lt;/h2&gt;

&lt;p&gt;参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/wpf-VisualBrush-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98.html&quot;&gt;wpf VisualBrush 已知问题 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Jul 2019 12:31:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/visual-brush-refresh-views-only-but-not-layout.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/visual-brush-refresh-views-only-but-not-layout.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>一文看懂 .NET 的异常处理机制、原则以及最佳实践</title>
        <description>&lt;p&gt;什么时候该抛出异常，抛出什么异常？什么时候该捕获异常，捕获之后怎么处理异常？你可能已经使用异常一段时间了，但对 .NET/C# 的异常机制依然有一些疑惑。那么，可以阅读本文。&lt;/p&gt;

&lt;p&gt;本文适用于已经入门 .NET/C# 开发，已经开始在实践中抛出和捕获异常，但是对 .NET 异常机制的用法以及原则比较模糊的小伙伴。通过阅读本文，小伙伴们可以迅速在项目中使用比较推荐的异常处理原则来处理异常。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;快速了解-net-的异常机制&quot;&gt;快速了解 .NET 的异常机制&lt;/h2&gt;

&lt;h3 id=&quot;exception-类&quot;&gt;Exception 类&lt;/h3&gt;

&lt;p&gt;我们大多数小伙伴可能更多的使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 的类型、&lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 属性、&lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace&lt;/code&gt; 以及内部异常来定位问题，但其实 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 类型还有更多的信息可以用于辅助定位问题。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 用来描述异常原因的详细信息
    &lt;ul&gt;
      &lt;li&gt;如果你捕捉到了异常，一般使用这段描述能知道发生的大致原因。&lt;/li&gt;
      &lt;li&gt;如果你准备抛出异常，在这个信息里面记录能帮助调试问题的详细文字信息。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace&lt;/code&gt; 包含用来确定错误位置的堆栈跟踪（当有调试信息如 PDB 时，这里就会包含源代码文件名和源代码行号）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InnerException&lt;/code&gt; 包含内部异常信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Source&lt;/code&gt; 这个属性包含导致错误的应用程序或对象的名称&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Data&lt;/code&gt; 这是一个字典，可以存放基于键值的任意数据，帮助在异常信息中获得更多可以用于调试的数据&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HelpLink&lt;/code&gt; 这是一个 url，这个 url 里可以提供大量用于说明此异常原因的信息&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你自己写一个自定义异常类，那么你可以在自定义的异常类中记录更多的信息。然而大多数情况下我们都考虑使用 .NET 中自带的异常类，因此可以充分利用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 类中的已有属性在特殊情况下报告更详细的利于调试的异常信息。&lt;/p&gt;

&lt;h3 id=&quot;捕捉异常&quot;&gt;捕捉异常&lt;/h3&gt;

&lt;p&gt;捕捉异常的基本语法是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 可能引发异常的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 处理一种类型的异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 处理另一种类的异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;除此之外，还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 关键字用于筛选异常：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 可能引发异常的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 处理一种类型的异常，并且此文件扩展名为 .png。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 处理一种类型的异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;无论是否有带 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 关键字，都是前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块匹配的时候执行匹配的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块而无视后面可能也匹配的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块。&lt;/p&gt;

&lt;p&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 块中抛出异常，那么此异常将被忽略，&lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; 中的表达式值视为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。有个但是，请看：&lt;a href=&quot;/post/try-catch-when-causes-app-crash&quot;&gt;.NET Framework 的 bug？try-catch-when 中如果 when 语句抛出异常，程序将彻底崩溃 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;引发异常&quot;&gt;引发异常&lt;/h3&gt;

&lt;p&gt;引发异常使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 关键字。只是注意如果要重新抛出异常，请使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw;&lt;/code&gt; 语句或者将原有异常作为内部异常。&lt;/p&gt;

&lt;h3 id=&quot;创建自定义异常&quot;&gt;创建自定义异常&lt;/h3&gt;

&lt;p&gt;如果你只是随便在业务上创建一个异常，那么写一个类继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyCustomException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCustomProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyCustomException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCustomProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，如果你需要写一些比较通用抽象的异常（用于被继承），或者在底层组件代码中写自定义异常，那么就建议考虑写全异常的所有构造函数，并且加上可序列化：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Serializable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InvalidDepartmentException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidDepartmentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidDepartmentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidDepartmentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;innerException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;innerException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果异常需要跨应用程序域、跨进程或者跨计算机抛出，就需要能被序列化。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidDepartmentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SerializationInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StreamingContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在创建自定义异常的时候，建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;名称以 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 结尾&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 属性的值是一个句子，用于描述异常发生的原因。&lt;/li&gt;
  &lt;li&gt;提供帮助诊断错误的属性。&lt;/li&gt;
  &lt;li&gt;尽量写全四个构造函数，前三个方便使用，最后一个用于序列化异常（新的异常类应可序列化）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;finally&quot;&gt;finally&lt;/h3&gt;

&lt;h3 id=&quot;异常堆栈跟踪&quot;&gt;异常堆栈跟踪&lt;/h3&gt;

&lt;p&gt;堆栈跟踪从引发异常的语句开始，到捕获异常的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 语句结束。&lt;/p&gt;

&lt;p&gt;利用这一点，你可以迅速找到引发异常的那个方法，也能找到是哪个方法中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 捕捉到的这个异常。&lt;/p&gt;

&lt;h2 id=&quot;异常处理原则&quot;&gt;异常处理原则&lt;/h2&gt;

&lt;h3 id=&quot;try-catch-finally&quot;&gt;try-catch-finally&lt;/h3&gt;

&lt;p&gt;我们第一个要了解的异常处理原则是——明确 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 的用途！&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块中，编写可能会发生异常的代码。&lt;/p&gt;

&lt;p&gt;最好的情况是，你只将可能会发生异常的代码放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块中，当然实际应用的时候可能会需要额外放入一些相关代码。但是如果你将多个可能发生异常的代码放到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块中，那么将来定位问题的时候你就会很抓狂（尤其是多个异常还是一个类别的时候）。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块的作用是用来 “恢复错误” 的，是用来 “恢复错误” 的，是用来 “恢复错误” 的。&lt;/p&gt;

&lt;p&gt;如果你在 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块中先更改了类的状态，随后出了异常，那么最好能将状态改回来——这可以避免这个类型或者应用程序的其他状态出现不一致——这很容易造成应用程序“雪崩”。举一个例子：我们写一个程序有简洁模式和专业模式，在从简洁模式切换到专业模式的时候，我们设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsProfessionalMode&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，但随后出现了异常导致没有成功切换为专业模式；然而接下来所有的代码在执行时都判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsProfessionalMode&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 状态不正确，于是执行了一些非预期的操作，甚至可能用到了很多专业模式中才会初始化的类型实例（然而没有完成初始化），产生大量的额外异常；我们说程序雪崩了，多数功能再也无法正常使用了。&lt;/p&gt;

&lt;p&gt;当然如果任务已全部完成，仅仅在对外通知的时候出现了异常，那么这个时候不需要恢复状态，因为实际上已经完成了任务。&lt;/p&gt;

&lt;p&gt;你可能会有些担心如果我没有任何手段可以恢复错误怎么办？那这个时候就不要处理异常！——如果不知道如何恢复错误，请不要处理异常！让异常交给更上一层的模块处理，或者交给整个应用程序全局异常处理模块进行统一处理（这个后面会讲到）。&lt;/p&gt;

&lt;p&gt;另外，异常不能用于在正常执行过程中更改程序的流程。异常只能用于报告和处理错误条件。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 块的作用是清理资源。&lt;/p&gt;

&lt;p&gt;虽然 .NET 的垃圾回收机制可以在回收类型实例的时候帮助我们回收托管资源（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileStream&lt;/code&gt; 类打开的文件），但那个时机不可控。因此我们需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 块中确保资源可被回收，这样当重新使用这个文件的时候能够立刻使用而不会被占用。&lt;/p&gt;

&lt;p&gt;一段异常处理代码中可能没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块而有 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 块，这个时候的重点是清理资源，通常也不知道如何正确处理这个错误。&lt;/p&gt;

&lt;p&gt;一段异常处理代码中也可能 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块留空，而只在 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 里面写代码，这是为了“线程终止”安全考虑。在 .NET Core 中由于不支持线程终止因此可以不用这么写。详情可以参考：&lt;a href=&quot;/post/empty-try-block&quot;&gt;.NET/C# 异常处理：写一个空的 try 块代码，而把重要代码写到 finally 中（Constrained Execution Regions） - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;该不该引发异常&quot;&gt;该不该引发异常？&lt;/h3&gt;

&lt;p&gt;什么情况下该引发异常？答案是——这真的是一个异常情况！&lt;/p&gt;

&lt;p&gt;于是，我们可能需要知道什么是“异常情况”。&lt;/p&gt;

&lt;p&gt;一个可以参考的判断方法是——判断这件事发生的频率：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果这件事并不常见，当它发生时确实代表发生了一个错误，那么这件事情就可以认为是异常。&lt;/li&gt;
  &lt;li&gt;如果这件事经常发生，代码中正常情况就应该处理这件事情，那么这件事情就不应该被认为是异常（而是正常流程的一部分）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;例如这些情况都应该认为是异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;方法中某个参数不应该传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时但传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这是开发者使用这个方法时没有遵循此方法的契约导致的，让开发者改变调用此方法的代码就可以完全避免这件事情发生&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而下面这些情况则不应该认为是异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;用户输入了一串字符，你需要将这串字符转换为数字
    &lt;ul&gt;
      &lt;li&gt;用户输入的内容本身就千奇百怪，出现非数字的输入再正常不过了，对非数字的处理本就应该成为正常流程的一部分&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于这些不应该认为是异常的情况，编写的代码就应该尽可能避免异常。&lt;/p&gt;

&lt;p&gt;有两种方法来避免异常：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;先判断再使用。
    &lt;ul&gt;
      &lt;li&gt;例如读取文件之前，先判断文件是否存在；例如读取文件流时先判断是否已到达文件末尾。&lt;/li&gt;
      &lt;li&gt;如果提前判断的成本过高，可采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryDo&lt;/code&gt; 模式来完成，例如字符串转数字中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryParse&lt;/code&gt; 方法，字典中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryGetValue&lt;/code&gt; 方法。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;对极为常见的错误案例返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;（或默认值），而不是引发异常。极其常见的错误案例可被视为常规控制流。通过在这些情况下返回 NULL（或默认值），可最大程度地减小对应用的性能产生的影响。（后面会专门说 null）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而当存在下列一种或多种情况时，应引发异常：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;方法无法完成其定义的功能。&lt;/li&gt;
  &lt;li&gt;根据对象的状态，对某个对象进行不适当的调用。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;请勿有意从自己的源代码中引发 &lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.exception&quot;&gt;System.Exception&lt;/a&gt;、&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.systemexception&quot;&gt;System.SystemException&lt;/a&gt;、&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.nullreferenceexception&quot;&gt;System.NullReferenceException&lt;/a&gt; 或 &lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.indexoutofrangeexception&quot;&gt;System.IndexOutOfRangeException&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;该不该捕获异常&quot;&gt;该不该捕获异常？&lt;/h3&gt;

&lt;p&gt;在前面 &lt;a href=&quot;#try-catch-finally&quot;&gt;try-catch-finally&lt;/a&gt; 小节中，我们提到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块中应该写哪些代码，那里其实已经说明了哪些情况下应该处理异常，哪些情况下不应该处理异常。一句总结性的话是——如果知道如何从错误中恢复，那么就捕获并处理异常，否则交给更上层的业务去捕获异常；如果所有层都不知道如何处理异常，就交给全局异常处理模块进行处理。&lt;/p&gt;

&lt;h3 id=&quot;应用程序全局处理异常&quot;&gt;应用程序全局处理异常&lt;/h3&gt;

&lt;p&gt;对于 .NET 程序，无论是 .NET Framework 还是 .NET Core，都有下面这三个可以全局处理的异常。这三个都是事件，可以自行监听。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.UnhandledException&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;应用程序域未处理的异常，任何线程中未处理掉的异常都会进入此事件中&lt;/li&gt;
      &lt;li&gt;当这里能够收到事件，意味着应用程序现在频临崩溃的边缘（从设计上讲，都到这里了，也再没有任何代码能够使得程序从错误中恢复了）&lt;/li&gt;
      &lt;li&gt;不过也可以&lt;a href=&quot;/post/prevent-app-crash-by-background-thread&quot;&gt;配置 legacyUnhandledExceptionPolicy 防止后台线程抛出的异常让程序崩溃退出&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;建议在这个事件中记录崩溃日志，然后对应用程序进行最后的拯救恢复操作（例如保存用户的文档数据）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.FirstChanceException&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;应用程序域中的第一次机会异常&lt;/li&gt;
      &lt;li&gt;我们前面说过，一个异常被捕获时，其堆栈信息将包含从 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 块到 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块之间的所有帧，而在第一次机会异常事件中，只是刚刚 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 出来，还没有被任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块捕捉，因此在这个事件中堆栈信息永远只会包含一帧（不过可以稍微变通一下&lt;a href=&quot;/post/how-to-get-the-full-stacktrace-of-an-first-chance-exception&quot;&gt;在第一次机会异常 FirstChanceException 中获取比较完整的异常堆栈&lt;/a&gt;）&lt;/li&gt;
      &lt;li&gt;注意第一次机会异常事件即便异常会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 也会引发，因为它引发在 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 之前&lt;/li&gt;
      &lt;li&gt;不要认为异常已经被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 就万事大吉可以无视这个事件了。前面我们说过异常仅在真的是异常的情况才应该引发，因此如果这个事件中引发了异常，通常也真的意味着发生了错误（差别只是我们能否从错误中恢复而已）。如果你经常在正常的操作中发现可以通过此事件监听到第一次机会异常，那么一定是应用程序或框架中的异常设计出了问题（可能把正常应该处理的流程当作了异常，可能内部实现代码错误，可能出现了使用错误），这种情况一定是要改代码修 Bug 的。而一些被认为是异常的情况下收到此事件则是正常的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TaskScheduler.UnobservedTaskException&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字编写异步代码的时候，如果一直有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 传递，那么异常始终可以被处理到；但中间有异步任务没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 导致异常没有被传递的时候，就会引发此事件。&lt;/li&gt;
      &lt;li&gt;如果在此事件中监听到异常，通常意味着代码中出现了不正确的 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的使用（要么应该修改实现避免异常，要么应该正确处理异常并从中恢复错误）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于 GUI 应用程序，还可以监听 UI 线程上专属的全局异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;WPF：&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.DispatcherUnhandledException&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.UnhandledException&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Windows Forms：&lt;code class=&quot;highlighter-rouge&quot;&gt;Application.ThreadException&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于这些全局异常的处理方式和示例代码，可以参阅博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/iron_ye/article/details/82913025&quot;&gt;WPF UnhandledException - Iron 的博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;抛出哪些异常&quot;&gt;抛出哪些异常？&lt;/h3&gt;

&lt;p&gt;任何情况下都不应该抛出这些异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;过于抽象，以至于无法表明其含义
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 这可是顶级基类，这都抛出来了，使用者再也无法正确地处理此异常了&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SystemException&lt;/code&gt; 这是各种异常的基类，本身并没有明确的意义&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationException&lt;/code&gt; 这是各种异常的基类，本身并没有明确的意义&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;由 CLR 引发的异常
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 试图在空引用上执行某些方法，除了告诉实现者出现了意料之外的 null 之外，没有什么其它价值了&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IndexOutOfRangeException&lt;/code&gt; 使用索引的时候超出了边界&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidCastException&lt;/code&gt; 表示试图对某个类型进行强转但类型不匹配&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StackOverflow&lt;/code&gt; 表示栈溢出，这通常说明实现代码的时候写了不正确的显式或隐式的递归&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OutOfMemoryException&lt;/code&gt; 表示托管堆中已无法分出期望的内存空间，或程序已经没有更多内存可用了&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AccessViolationException&lt;/code&gt; 这说明使用非托管内存时发生了错误&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BadImageFormatException&lt;/code&gt; 这说明了加载的 dll 并不是期望中的托管 dll&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TypeLoadException&lt;/code&gt; 表示类型初始化的时候发生了错误&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;.NET 设计失误
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FormatException&lt;/code&gt; 因为当它抛出来时无法准确描述到底什么错了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先是你自己不应该抛出这样的异常。其次，你如果在运行中捕获到了上面这些异常，那么代码一定是写得有问题。&lt;/p&gt;

&lt;p&gt;如果是捕获到了上面 CLR 的异常，那么有两种可能：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你的代码编写错误（例如本该判空的代码没有判空，又如索引数组超出界限）&lt;/li&gt;
  &lt;li&gt;你使用到的别人写的代码编写错误（那你就需要找到它改正，或者如果开源就去开源社区中修复吧）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而一旦捕获到了上面其他种类的异常，那就找到抛这个异常的人，然后对它一帧狂扁即可。&lt;/p&gt;

&lt;p&gt;其他的异常则是可以抛出的，只要你可以准确地表明错误原因。&lt;/p&gt;

&lt;p&gt;另外，尽量不要考虑抛出聚合异常 &lt;code class=&quot;highlighter-rouge&quot;&gt;AggregateException&lt;/code&gt;，而是优先使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt; 抛出其内部异常。详见：&lt;a href=&quot;/post/exceptiondispatchinfo-capture-throw&quot;&gt;使用 ExceptionDispatchInfo 捕捉并重新抛出异常 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;异常的分类&quot;&gt;异常的分类&lt;/h3&gt;

&lt;p&gt;在 &lt;a href=&quot;#该不该引发异常？&quot;&gt;该不该引发异常&lt;/a&gt; 小节中我们说到一个异常会被引发，是因为某个方法声称的任务没有成功完成（失败），而失败的原因有四种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;方法的使用者用错了（没有按照方法的契约使用）&lt;/li&gt;
  &lt;li&gt;方法的执行代码写错了&lt;/li&gt;
  &lt;li&gt;方法执行时所在的环境不符合预期&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;简单说来，就是：使用错误，实现错误、环境错误。&lt;/p&gt;

&lt;p&gt;使用错误：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt; 表示参数使用错了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt; 表示参数不应该传入 null&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentOutOfRangeException&lt;/code&gt; 表示参数中的序号超出了范围&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidEnumArgumentException&lt;/code&gt; 表示参数中的枚举值不正确&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt; 表示当前状态下不允许进行此操作（也就是说存在着允许进行此操作的另一种状态）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ObjectDisposedException&lt;/code&gt; 表示对象已经 Dispose 过了，不能再使用了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotSupportedException&lt;/code&gt; 表示不支持进行此操作（这是在说不要再试图对这种类型的对象调用此方法了，不支持）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt; 表示在此平台下不支持（如果程序跨平台的话）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt; 表示此功能尚在开发中，暂时请勿使用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实现错误：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;前面由 CLR 抛出的异常代码主要都是实现错误&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 试图在空引用上执行某些方法，除了告诉实现者出现了意料之外的 null 之外，没有什么其它价值了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IndexOutOfRangeException&lt;/code&gt; 使用索引的时候超出了边界&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidCastException&lt;/code&gt; 表示试图对某个类型进行强转但类型不匹配&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StackOverflowException&lt;/code&gt; 表示栈溢出，这通常说明实现代码的时候写了不正确的显式或隐式的递归&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OutOfMemoryException&lt;/code&gt; 表示托管堆中已无法分出期望的内存空间，或程序已经没有更多内存可用了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AccessViolationException&lt;/code&gt; 这说明使用非托管内存时发生了错误&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BadImageFormatException&lt;/code&gt; 这说明了加载的 dll 并不是期望中的托管 dll&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TypeLoadException&lt;/code&gt; 表示类型初始化的时候发生了错误&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;环境错误：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt; 下的各种子类&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Win32Exception&lt;/code&gt; 下的各种子类&lt;/li&gt;
  &lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，还剩下一些不应该抛出的异常，例如过于抽象的异常和已经过时的异常，这在前面一小结中有说明。&lt;/p&gt;

&lt;h2 id=&quot;其他&quot;&gt;其他&lt;/h2&gt;

&lt;h3 id=&quot;一些常见异常的原因和解决方法&quot;&gt;一些常见异常的原因和解决方法&lt;/h3&gt;

&lt;p&gt;在平时的开发当中，你可能会遇到这样一些异常，它不像是自己代码中抛出的那些常见的异常，但也不包含我们自己的异常堆栈。&lt;/p&gt;

&lt;p&gt;这里介绍一些常见这些异常的原因和解决办法。&lt;/p&gt;

&lt;h4 id=&quot;accessviolationexception&quot;&gt;AccessViolationException&lt;/h4&gt;

&lt;p&gt;当出现此异常时，说明非托管内存中发生了错误。如果要解决问题，需要从非托管代码中着手调查。&lt;/p&gt;

&lt;p&gt;这个异常是访问了不允许的内存时引发的。在原因上会类似于托管中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;。&lt;/p&gt;

&lt;h4 id=&quot;filenotfoundexception&quot;&gt;FileNotFoundException&lt;/h4&gt;

&lt;h3 id=&quot;捕捉非-cls-异常&quot;&gt;捕捉非 CLS 异常&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/exceptions/&quot;&gt;Handling and throwing exceptions in .NET - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/&quot;&gt;Exceptions and Exception Handling - C# Programming Guide - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions&quot;&gt;Design Guidelines for Exceptions - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Jul 2019 00:33:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 很少人知道的科技</title>
        <description>&lt;p&gt;本文介绍不那么常见的 WPF 相关的知识。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;在-c-代码中创建-datatemplate&quot;&gt;在 C# 代码中创建 DataTemplate&lt;/h2&gt;

&lt;p&gt;大多数时候我们只需要在 XAML 中就可以实现我们想要的各种界面效果。这使得你可能已经不知道如何在 C# 代码中创建同样的内容。&lt;/p&gt;

&lt;p&gt;比如在代码中创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt;，主要会使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElementFactory&lt;/code&gt; 类型。&lt;/p&gt;

&lt;p&gt;可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/Iron_Ye/article/details/83504358&quot;&gt;WPF 后台创建 DateTemplate - Iron 的博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;多个数据源合并为一个列表显示&quot;&gt;多个数据源合并为一个列表显示&lt;/h2&gt;

&lt;p&gt;WPF 提供 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompositionCollection&lt;/code&gt; 用于将多个列表合并为一个，以便在 WPF 界面的同一个列表中显示多个数据源的数据。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ListBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoListBox&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListBox.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;CollectionViewSource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Items1Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Items1}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;CollectionViewSource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Items2Source&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Items2}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ListBox.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListBox.ItemsSource&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;CompositeCollection&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;CollectionContainer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Collection=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Source={StaticResource Items1Source}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;CollectionContainer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Collection=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Source={StaticResource Items2Source}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListBoxItem&amp;gt;&lt;/span&gt;Walterlv End Item 1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ListBoxItem&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListBoxItem&amp;gt;&lt;/span&gt;Walterlv End Item 2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ListBoxItem&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/CompositeCollection&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ListBox.ItemsSource&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ListBox&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompositeCollection&lt;/code&gt; 的使用示例可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-a-compositecollection&quot;&gt;How to: Implement a CompositeCollection - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;神樹桜乃写了一份非 WPF 框架的版本，如果希望在非 WPF 程序中使用，可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/KodamaSakuno/Sakuno.Base/blob/master/src/Sakuno.Base/Collections/ConcatenatedCollectionView%60T.cs&quot;&gt;Sakuno.Base/ConcatenatedCollectionView`T.cs at master · KodamaSakuno/Sakuno.Base&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用附加属性做缓存避免内存泄漏&quot;&gt;使用附加属性做缓存，避免内存泄漏&lt;/h2&gt;

&lt;p&gt;在没有使用 WPF 的时候，如果我们要为一个对象添加属性或者行为，我们可能会使用字典来实现。但字典带来了内存泄漏的问题，要自己处理内存泄漏问题可能会写比较复杂的代码。&lt;/p&gt;

&lt;p&gt;然而，WPF 的附加属性可以非常容易地为对象添加属性或者行为，而且也不用担心内存泄漏问题。&lt;/p&gt;

&lt;p&gt;例如，我曾经用 WPF 来模拟 UWP 流畅设计（Fluent Design）中的光照效果，使用附加属性来管理此行为则完全不用担心内存泄漏问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用-conditionalweaktable-做非-wpf-版本的缓存&quot;&gt;使用 ConditionalWeakTable 做非 WPF 版本的缓存&lt;/h2&gt;

&lt;p&gt;如果你有一些非 WPF 的对象需要做类似 WPF 那种附加属性，那么可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt; 来实现，Key 是那个对象，而 Value 是你需要附加的属性或者行为。&lt;/p&gt;

&lt;p&gt;这里的引用关系是 Key 引用着 Value，如果 Key 被回收，那么 Value 也可以被回收。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/conditional-weak-table&quot;&gt;.NET/C# 使用 ConditionalWeakTable 附加字段（CLR 版本的附加属性，也可用用来当作弱引用字典 WeakDictionary）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用代码模拟触摸&quot;&gt;使用代码模拟触摸&lt;/h2&gt;

&lt;p&gt;WPF 默认情况下的触摸是通过 COM 组件 &lt;code class=&quot;highlighter-rouge&quot;&gt;PimcManager&lt;/code&gt; 获取到的，在&lt;a href=&quot;https://blog.lindexi.com/post/wpf-%E7%A6%81%E7%94%A8%E5%AE%9E%E6%97%B6%E8%A7%A6%E6%91%B8&quot;&gt;禁用实时触摸&lt;/a&gt;后会启用系统的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TOUCH&lt;/code&gt; 消息获取到，如果&lt;a href=&quot;https://blog.lindexi.com/post/win10-%E6%94%AF%E6%8C%81%E9%BB%98%E8%AE%A4%E6%8A%8A%E8%A7%A6%E6%91%B8%E6%8F%90%E5%8D%87%E9%BC%A0%E6%A0%87%E4%BA%8B%E4%BB%B6-%E6%89%93%E5%BC%80-pointer-%E6%B6%88%E6%81%AF&quot;&gt;开启了 Pointer 消息&lt;/a&gt;那么会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;POINTER&lt;/code&gt; 消息。&lt;/p&gt;

&lt;p&gt;我们可以继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;TouchDevice&lt;/code&gt; 来模拟触摸，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/wpf-%E6%A8%A1%E6%8B%9F%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87&quot;&gt;WPF 模拟触摸设备&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;模拟-uwp-界面&quot;&gt;模拟 UWP 界面&lt;/h2&gt;

&lt;p&gt;在现有的 Windowing API 下，系统中看起来非常接近系统级的窗口样式可能都是用不同技术模拟实现的，只是模拟得很像而已。&lt;/p&gt;

&lt;p&gt;如果要将 WPF 模拟得很像 UWP，可以参考我的这两篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-style-using-window-chrome&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-title-bar-buttons&quot;&gt;WPF 应用完全模拟 UWP 的标题栏按钮&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;模拟-fluent-design-特效&quot;&gt;模拟 Fluent Design 特效&lt;/h2&gt;

&lt;p&gt;目前 WPF 还不能直接使用 Windows 10 Fluent Design 特效。当然如果你的程序非常小，那么模拟一下也不会伤害太多性能：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&quot;&gt;在 Windows 10 上为 WPF 窗口添加模糊特效&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而充分利用 Fluent Design 的高性能，需要上 XAML Islands，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[Using the UWP XAML hosting API in a desktop application - Windows apps&lt;/td&gt;
          &lt;td&gt;Microsoft Docs](https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/using-the-xaml-hosting-api)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 11 Jul 2019 06:16:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/those-people-dont-know-about-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/those-people-dont-know-about-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如果不用 ReSharper，那么 Visual Studio 2019 能还原 ReSharper 多少功能呢？</title>
        <description>&lt;p&gt;本文只谈论 ReSharper 的那些常用功能中，Visual Studio 2019 能还原多少，主要提供给那些正在考虑不使用 ReSharper 插件的 Visual Studio 用户作为参考。毕竟 ReSharper 如此强大的功能是建立在每年缴纳不少的费用以及噩梦般占用 Visual Studio 性能的基础之上的。然而使用 Visual Studio 2019 社区版不搭配 ReSharper 则可以免费为开源社区做贡献。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;本文的内容分为三个部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Visual Studio 能完全还原的 ReSharper 的功能
    &lt;ul&gt;
      &lt;li&gt;可能 Visual Studio 在此功能上已经追赶上了 ReSharper&lt;/li&gt;
      &lt;li&gt;可能 Visual Studio 在此功能上虽然依然不如 ReSharper 完善，但缺少的部分几乎不影响体验&lt;/li&gt;
      &lt;li&gt;可能 Visual Studio 此功能比 ReSharper 更胜一筹&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Visual Studio 能部分还原 ReSharper 的功能
    &lt;ul&gt;
      &lt;li&gt;可能在多数场景中 Visual Studio 能获得 ReSharper 的此功能效果，在少数场景下不如 ReSharper&lt;/li&gt;
      &lt;li&gt;可能对多数人来说 Visual Studio 能获得 ReSharper 的此功能效果，对另一部分人来说无法替代 ReSharper&lt;/li&gt;
      &lt;li&gt;有可能 Visual Studio 在此功能上另辟蹊径比 ReSharper 更厉害，但综合效果不如 ReSharper&lt;/li&gt;
      &lt;li&gt;Visual Studio 此功能依然很弱，但可以通过安装免费的插件的方式补足&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Visual Studio 此功能依然比不上 ReSharper
    &lt;ul&gt;
      &lt;li&gt;可能是 Visual Studio 没有此功能&lt;/li&gt;
      &lt;li&gt;可能是 Visual Studio 此功能的实现方式上不如 ReSharper 快速、高效、简单&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;完美还原&quot;&gt;完美还原&lt;/h2&gt;

&lt;h3 id=&quot;无处不在的智能感知提示&quot;&gt;无处不在的智能感知提示&lt;/h3&gt;

&lt;p&gt;默认情况下，Visual Studio 只在你刚开始打字或者输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;(&lt;/code&gt; 的时候才出现智能感知提示，但是如果你使用 ReSharper 开发，你会发现智能感知提示无处不在（所以那么卡？）。&lt;/p&gt;

&lt;p&gt;实际上你也可以配置 Visual Studio 的智能感知在更多的情况下出现，请打开下面“工具”-&amp;gt;“选项”-&amp;gt;“文本编辑器”-&amp;gt;“C#”-&amp;gt;“IntelliSense”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-24-07-56-52.png&quot; alt=&quot;打开更多的智能感知提示时机&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开“键入字符后显示完成列表”和“删除字符后显示完成列表”。这样，你只要正在编辑，都会显示智能感知提示。&lt;/p&gt;

&lt;p&gt;另外，如果你当前需要打开智能感知提示，默认情况下使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + 空格键&lt;/code&gt; 可以打开。当然你也可以将其修改为 ReSharper 中常见的快捷键 &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt + 右箭头&lt;/code&gt;。方法是修改键盘快捷键中的 “” 项。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-10-20-07-38.png&quot; alt=&quot;完成列表&quot; /&gt;&lt;/p&gt;

&lt;p&gt;修改快捷键方法详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/customizing-keyboard-shortcuts-in-visual-studio&quot;&gt;如何快速自定义 Visual Studio 中部分功能的快捷键&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;IntelliCode&lt;/code&gt; 部分，可以选择打开更多的 &lt;code class=&quot;highlighter-rouge&quot;&gt;IntelliSense&lt;/code&gt; 完成项：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-10-19-14-53.png&quot; alt=&quot;IntelliCode&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在输入时即自动导入需要的命名空间&quot;&gt;在输入时即自动导入需要的命名空间&lt;/h3&gt;

&lt;p&gt;ReSharper 的智能感知提示包含所依赖的各种程序集中的类型，然而 Visual Studio 的智能感知则没有包含那些，只有顶部写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt; 的几个命名空间中的类型。&lt;/p&gt;

&lt;p&gt;Visual Studio 2019 中可以设置智能感知提示中“显示未导入命名空间中的项”。默认是没有开启的，当开启后，你将直接能在智能感知提示中看到原本 ReSharper 中才能有的编写任何类型的体验。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-14-16-33.png&quot; alt=&quot;智能感知中包含尚未导入的类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;默认情况下输入未知类型时只能完整输入类名然后使用重构快捷键将命名空间导入：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-14-21-06.png&quot; alt=&quot;只能通过重构导入命名空间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但开启了此选项后，只需要输入类名的一部分，哪怕此类型还没有写 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt; 将其导入，也能在智能感知提示中看到并且完成输入。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-14-22-28.png&quot; alt=&quot;可以导入命名空间的智能感知提示&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;提取局部变量&quot;&gt;提取局部变量&lt;/h3&gt;

&lt;p&gt;在 ReSharper 中，选中一段代码，如果这段代码可以返回一个值，那么可以使用重构快捷键（默认 Alt+Enter）生成一个局部变量。如果同样带代码块在此方法体中有多处，那么可以同时将多处代码一并提取出来成为一个布局变量。&lt;/p&gt;

&lt;p&gt;在 Visual Studio 中，也可以选中一段代码将其提取称一个局部变量：&lt;/p&gt;

&lt;h3 id=&quot;重命名标识符类名方法名属性名变量名等&quot;&gt;重命名标识符（类名/方法名/属性名/变量名等）&lt;/h3&gt;

&lt;p&gt;ReSharper 可以使用 Ctrl + R, R 快捷键重命名一个标识符。&lt;/p&gt;

&lt;p&gt;Visual Studio 中也是默认使用 F2 或者与 ReSharper 相同的 Ctrl + R, R 快捷键来重命名一个标识符。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-10-21-20-34.png&quot; alt=&quot;重命名标识符&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;可以还原&quot;&gt;可以还原&lt;/h2&gt;

&lt;p&gt;正在填坑……&lt;/p&gt;

&lt;h2 id=&quot;依然不足&quot;&gt;依然不足&lt;/h2&gt;

&lt;h3 id=&quot;大量的代码片段&quot;&gt;大量的代码片段&lt;/h3&gt;

&lt;p&gt;ReSharper 中自带了大量方便的代码片段，而且其代码片段的可定制性非常强，有很多可以只能完成的宏；而且还有后置式代码片段。&lt;/p&gt;

&lt;p&gt;然而 Visual Studio 自带的代码片段就弱很多，只能支持最基本的宏。&lt;/p&gt;

&lt;p&gt;不过可以通过下面一些插件通过数量来补足功能上的一些短板：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=josefpihrt-vscode.snippetica-csharp&quot;&gt;Snippetica for C# - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=jsakamoto.CMethodsCodeSnippets&quot;&gt;C# Methods Code Snippets - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 10 Jul 2019 14:03:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/visual-studio-vs-resharper.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/visual-studio-vs-resharper.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树</title>
        <description>&lt;p&gt;使用 Visual Studio 提供的 Syntax Visualizer，我们可以实时看到一个代码文件中的语法树。这对我们基于 Roslyn 编写静态分析和修改工具非常有帮助。本文将介绍如何安装它和使用它。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是 Roslyn 入门系列之一：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树（本文）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里是 Visual Studio 的语法可视化（Syntax Visualizer）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-18-20-51-14.png&quot; alt=&quot;Syntax Visualizer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;正在分析的代码文件是 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer/&quot;&gt;MSTestEnhancer&lt;/a&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContractTestContext.cs&lt;/code&gt;；也就是我的另一篇文章 &lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt; 中所采用的例子。&lt;/p&gt;

&lt;p&gt;语法可视化树中有三种不同颜色的节点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;蓝色：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.codeanalysis.syntaxnode?view=roslyn-dotnet&quot;&gt;SyntaxNode&lt;/a&gt;，表示声明、语句、子句和表达式等语法构造。&lt;/li&gt;
  &lt;li&gt;绿色：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.codeanalysis.syntaxtoken?view=roslyn-dotnet&quot;&gt;SyntaxToken&lt;/a&gt;，表示关键字、标识符、运算符等标点。&lt;/li&gt;
  &lt;li&gt;红色：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.codeanalysis.syntaxtrivia?view=roslyn-dotnet&quot;&gt;SyntaxTrivia&lt;/a&gt;，代表语法上不重要的信息，例如标记、预处理指令和注释之间的空格。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你是 Visual Studio 2017 / 2019，并且在安装 Visual Studio 时选择了 Visual Studio 扩展开发的工作负载，并且已经勾选了 .NET Compiler Platform SDK，那么你就已经安装好了。如果没有找到，请前往 &lt;a href=&quot;/post/how-to-prepare-visual-studio-extension-development-environment&quot;&gt;如何安装和准备 Visual Studio 扩展/插件开发环境 - walterlv&lt;/a&gt; 再安装。如果你的 Visual Studio 版本比较旧，则需要去 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.NETCompilerPlatformSDK&quot;&gt;.NET Compiler Platform SDK - Visual Studio Marketplace&lt;/a&gt; 下载安装。&lt;/p&gt;

&lt;p&gt;安装完之后，去“视图-&amp;gt;其它窗口”中就可以找到“Syntax Visualizer”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-18-20-59-08.png&quot; alt=&quot;视图-&amp;gt;其它窗口-&amp;gt;Syntax Visualizer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们在代码文件中任意地移动光标、选择代码块，都可以在 Syntax Visualizer 中看到对应的语法节点。这对我们基于 Roslyn 编写静态分析和修改工具非常有帮助。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/get-started/syntax-analysis&quot;&gt;Get started with syntax analysis (Roslyn APIs) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 07 Jul 2019 02:00:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/roslyn-syntax-visualizer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/roslyn-syntax-visualizer.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>如何安装和准备 Visual Studio 扩展/插件开发环境</title>
        <description>&lt;p&gt;因为很多涉及到 Visual Studio 插件开发相关的文章/博客需要以安装 Visual Studio 插件开发环境为基础，所以本文介绍如何安装 Visual Studio 插件开发环境，以简化那些博客中的内容。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;启动-visual-studio-安装程序&quot;&gt;启动 Visual Studio 安装程序&lt;/h2&gt;

&lt;p&gt;请在开始菜单中找到或者搜索 Visual Studio Installer，然后启动它：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-10-40.png&quot; alt=&quot;找到并且启动 Visual Studio Installer&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装-visual-studio-插件开发工作负载&quot;&gt;安装 Visual Studio 插件开发工作负载&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 的安装界面中选择“修改”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-12-15.png&quot; alt=&quot;修改&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在工作负载中找到并勾选 Visual Studio 扩展开发（英文版是 Visual Studio extension development），然后按下右下角的“修改”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-17-03.png&quot; alt=&quot;勾选 Visual Studio 扩展开发负载&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等待 Visual Studio 安装完 Visual Studio 扩展开发。如果提示重启计算机，那么就重启一下。&lt;/p&gt;

&lt;h2 id=&quot;如果你想开发基于-roslyn-的语法语义分析插件&quot;&gt;如果你想开发基于 Roslyn 的语法/语义分析插件&lt;/h2&gt;

&lt;p&gt;如果你想开发基于 Roslyn 的语法/语义分析插件，那么需要在选择了 Visual Studio 扩展开发工作负载之后，在右侧将可选的 .NET Compiler Platform SDK 也打上勾。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-07-09-57-09.png&quot; alt=&quot;.NET Compiler Platform SDK&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;体验-visual-studio-插件模板&quot;&gt;体验 Visual Studio 插件模板&lt;/h2&gt;

&lt;p&gt;如果你成功安装了 Visual Studio 扩展开发的工作负载，那么你在新建项目的时候就可以看到 Visual Studio 扩展开发相关的项目模板。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-20-14.png&quot; alt=&quot;Visual Studio 扩展开发相关模板&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 07 Jul 2019 01:58:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-prepare-visual-studio-extension-development-environment.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-prepare-visual-studio-extension-development-environment.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider</title>
        <description>&lt;p&gt;Roslyn 是 .NET 平台下十分强大的编译器，其提供的 API 也非常丰富好用。本文将基于 Roslyn 开发一个 C# 代码分析器，你不止可以将分析器作为 Visual Studio 代码分析和重构插件发布，还可以作为 NuGet 包发布。不管哪一种，都可以让我们编写的 C# 代码分析器工作起来并真正起到代码建议和重构的作用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文将教大家如何从零开始开发一个基于 Roslyn 的 C# 源代码分析器 Analyzer 和修改器 CodeFixProvider。可以作为 Visual Studio 插件安装和使用，也可以作为 NuGet 包安装到项目中使用（无需安装插件）。无论哪一种，你都可以在支持 Roslyn 分析器扩展的 IDE（如 Visual Studio）中获得如下面动图所展示的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-06-preview-of-roslyn-code-fix.gif&quot; alt=&quot;本文教大家可以做到的效果&quot; /&gt;&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;开发准备&quot;&gt;开发准备&lt;/h2&gt;

&lt;h3 id=&quot;安装-visual-studio-扩展开发工作负载&quot;&gt;安装 Visual Studio 扩展开发工作负载&lt;/h3&gt;

&lt;p&gt;你需要先安装 Visual Studio 的扩展开发工作负载，如果你还没有安装，那么请先阅读以下博客安装：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-23-39.png&quot; alt=&quot;Visual Studio 扩展开发&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-prepare-visual-studio-extension-development-environment&quot;&gt;如何安装和准备 Visual Studio 扩展/插件开发环境&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;创建一个分析器项目&quot;&gt;创建一个分析器项目&lt;/h3&gt;

&lt;p&gt;启动 Visual Studio，新建项目，然后在项目模板中找到 “Analyzer with Code Fix (.NET Standard)”，下一步。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-27-06.png&quot; alt=&quot;Analyzer with Code Fix 模板&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，取好项目名字之后，点击“创建”，你将来到 Visual Studio 的主界面。&lt;/p&gt;

&lt;p&gt;我为项目取的名称是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.Analyzers&lt;/code&gt;，接下来都将以此名称作为示例。你如果使用了别的名称，建议你自己找到名称的对应关系。&lt;/p&gt;

&lt;p&gt;在创建完项目之后，你可选可以更新一下项目的 .NET Standard 版本（默认是 1.3，建议更新为 2.0）以及几个 NuGet 包。&lt;/p&gt;

&lt;h3 id=&quot;首次调试&quot;&gt;首次调试&lt;/h3&gt;

&lt;p&gt;如果你现在按下 F5，那么将会启动一个 Visual Studio 的实验实例用于调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-53-50.png&quot; alt=&quot;Visual Studio 实验实例&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于我们是一个分析器项目，所以我们需要在第一次启动实验实例的时候新建一个专门用来测试的小型项目。&lt;/p&gt;

&lt;p&gt;简单起见，我新建一个 .NET Core 控制台项目。新建的项目如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-58-03.png&quot; alt=&quot;测试用的控制台项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们目前只是基于模板创建了一个分析器，而模板中自带的分析器功能是 “只要类型名称中有任何一个字符是小写的，就给出建议将其改为全部大写”。&lt;/p&gt;

&lt;p&gt;于是我们看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类名底下标了绿色的波浪线，我们将光标定位到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类名上，可以看到出现了一个 “小灯泡” 提示。按下重构快捷键（默认是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + .&lt;/code&gt;）后可以发现，我们的分析器项目提供的 “Make uppercase” 建议显示了出来。于是我们可以快速地将类名修改为全部大写。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-code-fix-make-upper-case.gif&quot; alt=&quot;模板中自带的分析器建议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为我们在前面安装了 Visual Studio 扩展开发的工作负载，所以可以在 “视图”-&amp;gt;“其他窗口” 中找到并打开 Syntax Visualizer 窗格。现在，请将它打开，因为接下来我们的代码分析会用得到这个窗格。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-22-42-05.png&quot; alt=&quot;打开语法可视化窗格&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果体验完毕，可以关闭 Visual Studio；当然也可以在我们的分析器项目中 Shift + F5 强制结束调试。&lt;/p&gt;

&lt;p&gt;下次调试的时候，我们不需要再次新建项目了，因为我们刚刚新建的项目还在我们新建的文件夹下。下次调试只要像下面那样再次打开这个项目测试就好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-23-29-33.png&quot; alt=&quot;打开历史记录中的项目&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解读模板自带的分析器项目&quot;&gt;解读模板自带的分析器项目&lt;/h2&gt;

&lt;h3 id=&quot;项目和解决方案&quot;&gt;项目和解决方案&lt;/h3&gt;

&lt;p&gt;在创建完项目之后，你会发现解决方案中有三个项目：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-20-46-27.png&quot; alt=&quot;Visual Studio 分析器解决方案&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Walterlv.Demo.Analyzers
    &lt;ul&gt;
      &lt;li&gt;分析器主项目，我们接下来分析器的主要逻辑代码都在这个项目中&lt;/li&gt;
      &lt;li&gt;这个项目在编译成功之后会生成一个 NuGet 包，安装了此包的项目将会运行我们的分析器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Walterlv.Demo.Analyzers.Vsix
    &lt;ul&gt;
      &lt;li&gt;Visual Studio 扩展项目，我们会在这里 Visual Studio 插件相关的信息&lt;/li&gt;
      &lt;li&gt;这个项目在便已成功之后会生成一个 Visual Studio 插件安装包，Visual Studio 安装了此插件后将会对所有正在编辑的项目运行我们的分析器&lt;/li&gt;
      &lt;li&gt;这个项目在默认情况下是启动项目（按下 F5 会启动这个项目调试），调试时会启动一个 Visual Studio 的实验实例&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Walterlv.Demo.Analyzers.Test
    &lt;ul&gt;
      &lt;li&gt;单元测试项目&lt;/li&gt;
      &lt;li&gt;模板为我们生成了比较多的辅助代码帮助我们快速编写用于测试我们分析器可用性的单元测试，我们接下来的代码质量也靠这个来保证&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在项目内部：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;WalterlvDemoAnalyzersAnalyzer.cs
    &lt;ul&gt;
      &lt;li&gt;模板中自带的分析器（Analyzer）的主要代码&lt;/li&gt;
      &lt;li&gt;我们什么都还没有写的时候，里面已经包含一份示例用的分析器，其功能是找到包含小写的类名。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;WalterlvDemoAnalyzersCodeFixProvider.cs
    &lt;ul&gt;
      &lt;li&gt;模板中自带的代码修改器（CodeFixProvider）的主要代码&lt;/li&gt;
      &lt;li&gt;我们什么都还没有写的时候，里面已经包含一份示例用的代码修改器，根据前面分析器中找到的诊断信息，给出修改建议，即只要类型名称中有任何一个字符是小写的，就给出建议将其改为全部大写&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Resources.resx
    &lt;ul&gt;
      &lt;li&gt;这里包含分析器建议使用的多语言信息&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-21-33-34.png&quot; alt=&quot;多语言资源文件&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;分析器代码analyzer&quot;&gt;分析器代码（Analyzer）&lt;/h3&gt;

&lt;p&gt;别看我们分析器文件中的代码很长，但实际上关键的信息并不多。&lt;/p&gt;

&lt;p&gt;我们现在还没有自行修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/code&gt; 类中的任何内容，而到目前位置这个类里面包含的最关键代码我提取出来之后是下面这些。为了避免你吐槽这些代码编译不通过，我将一部分的实现替换成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最关键的点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[DiagnosticAnalyzer(LanguageNames.CSharp)]&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;为 C# 语言提供诊断分析器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;override SupportedDiagnostics&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;返回此分析器支持的诊断规则&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;override Initialize&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;在此分析器初始化的时候执行某些代码&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;现在我们分别细化这些关键代码。为了简化理解，我将多语言全部替换成了实际的字符串值。&lt;/p&gt;

&lt;p&gt;重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;SupportedDiagnostics&lt;/code&gt; 的部分，创建并返回了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticDescriptor&lt;/code&gt; 类型的只读集合。目前只有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticDescriptor&lt;/code&gt;，名字是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rule&lt;/code&gt;，构造它的时候传入了一大堆字符串，包括分析器 Id、标题、消息提示、类型、级别、默认开启、描述信息。&lt;/p&gt;

&lt;p&gt;可以很容易看出，如果我们这个分析器带有多个诊断建议，那么在只读集合中返回多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticDescriptor&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoAnalyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Type name contains lowercase letters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MessageFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Type name '{0}' contains lowercase letters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Type names should be all uppercase.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Category&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Naming&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MessageFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticSeverity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isEnabledByDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Initialize&lt;/code&gt; 的部分，模板中注册了一个类名分析器，其实就是下面那个静态方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnalyzeSymbol&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSymbolAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalyzeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SymbolKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NamedType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在模板自带的实现中，这里判断类名是否包含小写字母，如果包含则创建一个新的诊断建议以改为大写字母。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;代码修改器codefixprovider&quot;&gt;代码修改器（CodeFixProvider）&lt;/h3&gt;

&lt;p&gt;代码修改器文件中的代码更长，但关键信息也没有增加多少。&lt;/p&gt;

&lt;p&gt;我们现在也没有自行修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/code&gt; 类中的任何内容，而到目前位置这个类里面包含的最关键代码我提取出来之后是下面这些。为了避免你吐槽这些代码编译不通过，我将一部分的实现替换成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExportCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CodeFixProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixableDiagnosticIds&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixAllProvider&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFixAllProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WellKnownFixAllProviders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchFixer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterCodeFixesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeFixContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最关键的点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(WalterlvDemoAnalyzersCodeFixProvider)), Shared]&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;为 C# 语言提供代码修改器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;override FixableDiagnosticIds&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;注意到前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/code&gt; 类型中有一个公共字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticId&lt;/code&gt; 吗？在这里返回，可以为那里分析器找到的代码提供修改建议&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;override GetFixAllProvider&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;在最简单的示例中，我们将仅仅返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;BatchFixer&lt;/code&gt;，其他种类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FixAllProvider&lt;/code&gt; 我将通过其他博客进行说明&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;override RegisterCodeFixesAsync&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;FixableDiagnosticIds&lt;/code&gt; 属性中我们返回的那些诊断建议这个方法中可以拿到，于是为每一个返回的诊断建议注册一个代码修改器（CodeFix）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在这个模板提供的例子中，&lt;code class=&quot;highlighter-rouge&quot;&gt;FixableDiagnosticIds&lt;/code&gt; 返回了 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/code&gt; 类中的公共字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticId&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixableDiagnosticIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RegisterCodeFixesAsync&lt;/code&gt; 中找到我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/code&gt; 类中找到的一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnostic&lt;/code&gt;，然后对这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnostic&lt;/code&gt; 注册一个代码修改（CodeFix）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterCodeFixesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeFixContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxRootAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnosticSpan&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Find the type declaration identified by the diagnostic.&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnosticSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AncestorsAndSelf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Register a code action that will invoke the fix.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterCodeFix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CodeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;createChangedSolution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeUppercaseAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;equivalenceKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeUppercaseAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeDecl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 将类名改为全大写，然后返回解决方案。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;开发自己的分析器analyzer&quot;&gt;开发自己的分析器（Analyzer）&lt;/h2&gt;

&lt;h3 id=&quot;一个简单的目标&quot;&gt;一个简单的目标&lt;/h3&gt;

&lt;p&gt;作为示例，我们写一个属性转换分析器，将自动属性转换为可通知属性。&lt;/p&gt;

&lt;p&gt;就是像以下上面的一种属性转换成下面的一种：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我们写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 方法，有没有这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 方法存在对我们后面写的分析器其实没有任何影响。不过你如果强迫症，可以看本文最后的“一些补充”章节，把 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 方法加进来。&lt;/p&gt;

&lt;h3 id=&quot;开始添加最基础的代码&quot;&gt;开始添加最基础的代码&lt;/h3&gt;

&lt;p&gt;于是，我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Initialize&lt;/code&gt; 方法中的内容改成我们期望的分析自动属性的语法节点分析。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSyntaxNodeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalyzeAutoProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeAutoProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNodeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 你可以在这一行打上一个断点，这样你可以观察 `context` 参数。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnalyzeAutoProperty&lt;/code&gt; 只是我们随便取的名字，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;SyntaxKind.PropertyDeclaration&lt;/code&gt; 是靠智能感知提示帮我找到的。&lt;/p&gt;

&lt;p&gt;现在我们来试着分析一个自动属性。&lt;/p&gt;

&lt;p&gt;按下 F5 调试，在新的调试的 Visual Studio 实验实例中，我们将鼠标光标放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;public string Foo { get; set; }&lt;/code&gt; 行上。如果我们提前在 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnalyzeAutoProperty&lt;/code&gt; 方法中打了断点，那么我们可以在此时观察到 &lt;code class=&quot;highlighter-rouge&quot;&gt;context&lt;/code&gt; 参数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-23-31-47.png&quot; alt=&quot;context 参数&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; 指示当前是否已取消分析&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Node&lt;/code&gt; 语法节点&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SemanticModel&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ContainingSymbol&lt;/code&gt; 语义分析节点&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Compilation&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Node.KindText&lt;/code&gt; 属性的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyDeclaration&lt;/code&gt;。还记得前面让你先提前打开 Syntax Visualizer 窗格吗？是的，我们可以在这个窗格中找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyDeclaration&lt;/code&gt; 节点。&lt;/p&gt;

&lt;p&gt;我们可以借助这个语法可视化窗格，找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyDeclaration&lt;/code&gt; 的子节点。当我们一级一级分析其子节点的语法的时候，便可以取得这个语法节点的全部所需信息（可见性、属性类型、属性名称），也就是具备生成可通知属性的全部信息了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-05-23-42-01.png&quot; alt=&quot;在语法可视化窗格中分析属性&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;添加分析自动属性的代码&quot;&gt;添加分析自动属性的代码&lt;/h3&gt;

&lt;p&gt;由于我们在前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Initialize&lt;/code&gt; 方法中注册了仅在属性声明语法节点的时候才会执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnalyzeAutoProperty&lt;/code&gt; 方法，所以我们在这里可以简单的开始报告一个代码分析 &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnostic&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyNode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReportDiagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/code&gt; 类的完整代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticAnalyzer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoAnalyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;自动属性&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_messageFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;这是一个自动属性&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LocalizableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;可以转换为可通知属性。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_category&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Usage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_rule&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_messageFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DiagnosticSeverity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isEnabledByDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSyntaxNodeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnalyzeAutoProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnalyzeAutoProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNodeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyNode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReportDiagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以发现代码并不多，现在运行，可以在光标落在属性声明的行时看到修改建议。如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-06-00-33-03.png&quot; alt=&quot;在属性上有修改建议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可能会觉得有些不满，看起来似乎只有我们写的那些标题和描述在工作。但实际上你还应该注意到这些：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DiagnosticId&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;_messageFormat&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;_description&lt;/code&gt; 已经工作起来了；&lt;/li&gt;
  &lt;li&gt;只有光标在属性声明的语句块时，这个提示才会出现，因此说明我们的已经找到了正确的代码块了；&lt;/li&gt;
  &lt;li&gt;不要忘了我们还有个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CodeFixProvider&lt;/code&gt; 没有写呢，你现在看到的依然还在修改大小写的部分代码是那个类（&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/code&gt;）里的。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;开发自己的代码修改器codefixprovider&quot;&gt;开发自己的代码修改器（CodeFixProvider）&lt;/h2&gt;

&lt;p&gt;现在，我们开始进行代码修改，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/code&gt; 类改成我们希望的将属性修改为可通知属性的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExportCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LanguageNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CodeFixProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;转换为可通知属性&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixableDiagnosticIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvDemoAnalyzersAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiagnosticId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixAllProvider&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFixAllProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WellKnownFixAllProviders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchFixer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterCodeFixesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeFixContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxRootAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterCodeFix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CodeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;createChangedSolution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertToNotificationProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;equivalenceKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;diagnostic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertToNotificationProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PropertyDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 获取文档根语法节点。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxRootAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 生成可通知属性的语法节点集合。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newNodes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateNotificationProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 将可通知属性的语法节点插入到原文档中形成一份中间文档。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediateRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertNodesAfter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;newNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 将中间文档中的自动属性移除形成一份最终文档。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediateRoot&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intermediateRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxRemoveOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeepNoTrivia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 将原来解决方案中的此份文档换成新文档以形成新的解决方案。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDocumentSyntaxRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertToNotificationProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PropertyDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这个类型暂时留空，因为这是真正的使用 Roslyn 生成语法节点的代码，虽然只会写一句话，但相当长。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还记得我们在前面解读 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/code&gt; 类型时的那些描述吗？我们现在为一个诊断 &lt;code class=&quot;highlighter-rouge&quot;&gt;Diagnostic&lt;/code&gt; 注册了一个代码修改（CodeFix），并且其回调函数是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConvertToNotificationProperty&lt;/code&gt;。这是我们自己编写的一个方法。&lt;/p&gt;

&lt;p&gt;我在这个方法里面写的代码并不复杂，是获取原来的属性里的类型信息和属性名，然后修改文档，将新的文档返回。&lt;/p&gt;

&lt;p&gt;其中，我留了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNotificationProperty&lt;/code&gt; 方法为空，因为这是真正的使用 Roslyn 生成语法节点的代码，虽然只会写一句话，但相当长。&lt;/p&gt;

&lt;p&gt;于是我将这个方法单独写在了下面。将这两个部分拼起来（用下面方法替换上面同名的方法），你就能得到一个完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoAnalyzersCodeFixProvider&lt;/code&gt; 类的代码了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateNotificationProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FieldDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeListSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SyntaxTokenList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrivateKeyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VariableDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SeparatedList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VariableDeclarator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemicolonToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PropertyDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddModifiers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PublicKeyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAccessorListAccessors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AccessorDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetAccessorDeclaration&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpressionBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArrowExpressionClause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EqualsGreaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IdentifierName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithSemicolonToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemicolonToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AccessorDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetAccessorDeclaration&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpressionBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArrowExpressionClause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EqualsGreaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IdentifierName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SetValue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArgumentList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenParenToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SeparatedList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IdentifierName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithRefKindKeyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RefKeyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IdentifierName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CloseParenToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithSemicolonToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemicolonToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上本文并不会重点介绍如何使用 Roslyn 生成新的语法节点，因此我不会解释上面我是如何写出这样的语法节点来的，但如果你对照着语法可视化窗格（Syntax Visualizer）来看的话，也是不难理解为什么我会这么写的。&lt;/p&gt;

&lt;p&gt;在此类型完善之后，我们再 F5 启动调试，可以发现我们已经可以完成一个自动属性的修改了，可以按照预期改成一个可通知属性。&lt;/p&gt;

&lt;p&gt;你可以再看看下面的动图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-06-preview-of-roslyn-code-fix.gif&quot; alt=&quot;可以修改属性&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;发布&quot;&gt;发布&lt;/h3&gt;

&lt;h3 id=&quot;发布成-nuget-包&quot;&gt;发布成 NuGet 包&lt;/h3&gt;

&lt;p&gt;前往我们分析器主项目 Walterlv.Demo.Analyzers 项目的输出目录，因为本文没有改输出路径，所以在项目的 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 文件夹下。我们可以找到每次编译产生的 NuGet 包。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-06-09-08-43.png&quot; alt=&quot;已经打出来的 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你不知道如何将此 NuGet 包发布到 &lt;a href=&quot;https://www.nuget.org/&quot;&gt;nuget.org&lt;/a&gt;，请在文本中回复，也许我需要再写一篇博客讲解如何推送。&lt;/p&gt;

&lt;h3 id=&quot;发布到-visual-studio-插件商店&quot;&gt;发布到 Visual Studio 插件商店&lt;/h3&gt;

&lt;p&gt;前往我们分析器的 Visual Studio 插件项目 Walterlv.Demo.Analyzers.Vsix 项目的输出目录，因为本文没有改输出路径，所以在项目的 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 文件夹下。我们可以找到每次编译产生的 Visual Studio 插件安装包。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-06-09-10-26.png&quot; alt=&quot;已经打出来的 Visual Studio 插件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你不知道如何将此 Visual Studio 插件发布到 &lt;a href=&quot;https://marketplace.visualstudio.com/&quot;&gt;Visual Studio Marketplace&lt;/a&gt;，请在文本中回复，也许我需要再写一篇博客讲解如何推送。&lt;/p&gt;

&lt;h2 id=&quot;一些补充&quot;&gt;一些补充&lt;/h2&gt;

&lt;h3 id=&quot;辅助源代码&quot;&gt;辅助源代码&lt;/h3&gt;

&lt;p&gt;前面我们提到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 这个方法，这是为了写一个可通知对象。为了拥有这个方法，请在我们的测试项目中添加下面这两个文件：&lt;/p&gt;

&lt;p&gt;一个可通知类文件 NotificationObject.cs：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.TestForAnalyzer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NotificationObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyPropertyChanged&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallerMemberName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyChangedEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个用于分析器测试的类 Demo.cs：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.TestForAnalyzer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotificationObject&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;示例代码仓库&quot;&gt;示例代码仓库&lt;/h3&gt;

&lt;p&gt;代码仓库在我的 Demo 项目中，注意协议是 &lt;a href=&quot;https://github.com/996icu/996.ICU/blob/master/LICENSE&quot;&gt;996.ICU&lt;/a&gt; 哟！&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.Demo.Analyzers&quot;&gt;walterlv.demo/Walterlv.Demo.Analyzers at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;别忘了单元测试&quot;&gt;别忘了单元测试&lt;/h3&gt;

&lt;p&gt;别忘了我们一开始创建仓库的时候有一个单元测试项目，而我们全文都没有讨论如何充分利用其中的单元测试。我将在其他的博客中说明如何编写和使用分析器项目的单元测试。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.meziantou.net/writing-a-roslyn-analyzer.htm&quot;&gt;Writing a Roslyn analyzer - Meziantou’s blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dogschasingsquirrels.com/2014/08/04/code-generation-with-roslyn-fields-and-properties/&quot;&gt;Code Generation with Roslyn – Fields and Properties - Dogs Chasing Squirrels&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 07 Jul 2019 01:52:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/develop-a-code-analyzer-for-both-nuget-and-visual-studio-extension.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/develop-a-code-analyzer-for-both-nuget-and-visual-studio-extension.html</guid>
        
        
        <category>roslyn</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何快速创建 Visual Studio 代码片段？</title>
        <description>&lt;p&gt;使用 Visual Studio 的代码片段功能，我们可以快速根据已有模板创建出大量常用的代码出来。ReSharper 已经自带了一份非常好用的代码片段工具，不过使用 ReSharper 创建出来的代码片段只能用在 ReSharper 插件中。如果团队当中有一些小伙伴没有 ReSharper（毕竟很贵），那么也可以使用到 Visual Studio 原生的代码片段。&lt;/p&gt;

&lt;p&gt;Visual Studio 的官方文档有演示如何创建 Visual Studio 的代码片段，不过上手成本真的很高。本文介绍如何快速创建 Visual Studio 代码片段，并不需要那么麻烦。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;visual-studio-的代码片段管理器&quot;&gt;Visual Studio 的代码片段管理器&lt;/h2&gt;

&lt;p&gt;Visual Studio 中代码片段管理器的入口在“工具”中。你可以参照下图找到代码片段管理器的入口。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-06-48.png&quot; alt=&quot;代码片段管理器入口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在打开代码片段管理器之后，你可以选择自己熟悉的语言。里面会列出当前语言中可以插入的各种代码片段的源。&lt;/p&gt;

&lt;p&gt;不过，Visual Studio 并没有提供创建代码片段的方法。在这个管理器里面，你只能导入已经存在的代码片段，并不能直接进行编辑。&lt;/p&gt;

&lt;p&gt;官方文档提供了创建代码片段的方法，就在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/code-snippets&quot;&gt;Code snippets - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你只需要看一看就知道这其实是非常繁琐的创建方式，你几乎在手工编写本来是给机器阅读的代码。&lt;/p&gt;

&lt;p&gt;我们创建代码片段其实只是关注代码片段本身，那么有什么更快速的方法呢？&lt;/p&gt;

&lt;p&gt;方法是安装插件。&lt;/p&gt;

&lt;h2 id=&quot;snippet-designer-插件&quot;&gt;Snippet Designer 插件&lt;/h2&gt;

&lt;p&gt;请去 Visual Studio 的扩展管理器中安装插件，或者去 Visual Studio 的插件市场中下载安装插件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vs-publisher-2795.SnippetDesigner&quot;&gt;Snippet Designer - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-11-16.png&quot; alt=&quot;在扩展管理器中安装插件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在安装完插件之后（需要重新启动 Visual Studio 以完成安装），你就可以直接在 Visual Studio 中创建和编辑代码片段了。&lt;/p&gt;

&lt;h2 id=&quot;创建代码片段&quot;&gt;创建代码片段&lt;/h2&gt;

&lt;p&gt;你需要去 Visual Studio 的“文件”-&amp;gt;“新建”-&amp;gt;“新建文件”中打开的模板选择列表中选择“Code Snippet”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-14-44.png&quot; alt=&quot;新建代码片段文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面，我演示创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug.WriteLine&lt;/code&gt; 代码片段的创建方法。&lt;/p&gt;

&lt;h3 id=&quot;编写一段代码&quot;&gt;编写一段代码&lt;/h3&gt;

&lt;p&gt;我将一段最简单的代码编写到了代码编辑窗格中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[section] text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;插入占位符&quot;&gt;插入占位符&lt;/h3&gt;

&lt;p&gt;实际上，这段代码中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;section&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 应该是占位符。那么如何插入占位符呢？&lt;/p&gt;

&lt;p&gt;选中需要成为占位符的文本，在这里是 &lt;code class=&quot;highlighter-rouge&quot;&gt;section&lt;/code&gt; ，然后鼠标右键，选择“Make Replacement”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-31-42.png&quot; alt=&quot;插入占位符&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样，在下方的列表中就会出现一个新的占位符。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-33-15.png&quot; alt=&quot;列表中出现占位符&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;设置文本占位符&quot;&gt;设置文本占位符&lt;/h3&gt;

&lt;p&gt;现在我们设置这个占位符的更多细节。比如在下图中，我设置了工具提示（即我们使用此代码片段的时候 Visual Studio 如何提示我们编写这个代码片段），设置了默认值（即没有写时应该是什么值）。设置了这只是一个文本文字，没有其他特别含义。设置这是可以编辑的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-46-07.png&quot; alt=&quot;设置更多信息&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用通常的方法，设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 也是一个占位符。&lt;/p&gt;

&lt;h3 id=&quot;设置类型占位符&quot;&gt;设置类型占位符&lt;/h3&gt;

&lt;p&gt;如果我们只是这样创建一个代码片段，而目标代码可能没有引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Diagnostics&lt;/code&gt; 命名空间，那么插入完之后手动引用这个命名空间体验可不好。那么如何让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 类可以带命名空间地插入呢？&lt;/p&gt;

&lt;p&gt;我们需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 也设置成占位符。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-49-59.png&quot; alt=&quot;将 Debug 也设置成占位符&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是这是可以自动生成的占位符，不需要用户输入，于是我们将其设置为不可编辑。同时，在“Function”一栏填写这是一个类型名称：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;SimpleTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-51-05.png&quot; alt=&quot;设置 Debug 占位符&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;转义--符号&quot;&gt;转义 &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; 符号&lt;/h3&gt;

&lt;p&gt;实际上用于调试的话，代码越简单功能越全越好。于是我希望 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug.WriteLine&lt;/code&gt; 上能够有一个字符串内插符号 &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;那么问题来了，&lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; 符号是表示代码片段中占位符的符号，那么如何输入呢？&lt;/p&gt;

&lt;p&gt;方法是——写两遍 &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt;。于是我们的代码片段现在是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[$section$] $text$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;保存代码片段&quot;&gt;保存代码片段&lt;/h3&gt;

&lt;p&gt;你可以随时按下 Ctrl+S 保存这个新建的代码片段。插件一个很棒的设计是，默认所在的文件夹就是 Visual Studio 中用来存放代码片段的文件夹。于是，你刚刚保存完就可以立刻在 Visual Studio 中看到效果了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-17-34.png&quot; alt=&quot;保存代码片段&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;导入代码片段&quot;&gt;导入代码片段&lt;/h2&gt;

&lt;p&gt;如果你将代码片段保存在插件给你的默认的位置，那么你根本不需要导入任何代码片段。但如果你曾经导出过代码片段或者保存在了其他的地方，那么就需要在代码片段管理器中导入这些代码片段文件了。&lt;/p&gt;

&lt;h3 id=&quot;使用代码片段&quot;&gt;使用代码片段&lt;/h3&gt;

&lt;p&gt;如果你前面使用了默认的保存路径，那么现在直接就可以开始使用了。&lt;/p&gt;

&lt;p&gt;使用我们在 Shortcut 中设置的字母组合可以插入代码片段：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-53-22.png&quot; alt=&quot;插入代码片段&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在插入完成之后，我们注意到此类型可以使用导入的命名空间前缀 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Diagnostics&lt;/code&gt;。如果没有导入此命名空间前缀，代码片段会自动加入。&lt;/p&gt;

&lt;p&gt;按下 Tab 键可以在多个占位符之间跳转，而使用回车键可以确认这个代码片段。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-12-03-08.png&quot; alt=&quot;插入后编辑的代码片段&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;管理代码片段&quot;&gt;管理代码片段&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 视图菜单的其他窗口中，可以找到“Snippet Explorer”，打开它可以管理已有的代码片段，包括 Visual Studio 中内置的那些片段。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-23-11-40-05.png&quot; alt=&quot;代码片段管理器&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;推荐-c-代码片段&quot;&gt;推荐 C# 代码片段&lt;/h2&gt;

&lt;p&gt;推荐另一款插件 Snippetica：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=josefpihrt.Snippetica&quot;&gt;Snippetica - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=josefpihrt-vscode.snippetica-csharp&quot;&gt;Snippetica for C# - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前者适用于 Visual Studio，后者适用于 Visual Studio Code。&lt;/p&gt;

&lt;p&gt;它自带了很多的 C# 代码片段，可以很大程度补充 Visual Studio 原生代码片段存在感低的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/code-snippets&quot;&gt;Code snippets - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[Walkthrough: Create a code snippet - Visual Studio&lt;/td&gt;
          &lt;td&gt;Microsoft Docs](https://docs.microsoft.com/en-us/visualstudio/ide/walkthrough-creating-a-code-snippet)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vs-publisher-2795.SnippetDesigner&quot;&gt;Snippet Designer - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mmanela/snippetdesigner&quot;&gt;mmanela/SnippetDesigner: The Snippet Designer is a plugin which enhances the Visual Studio IDE to allow a richer and more productive code snippet experience.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 05 Jul 2019 05:32:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-code-snippet-for-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-code-snippet-for-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 中设置当发生某个特定异常时进入断点（不借助 Visual Studio 的纯代码实现）</title>
        <description>&lt;p&gt;使用 Visual Studio 可以帮助我们在发生异常的时候中断，便于我们调试程序出现异常那一时刻的状态。如果没有 Visual Studio 的帮助（例如运行已发布的程序），当出现某个或某些特定异常的时候如何能够迅速进入中断的环境来调试呢？&lt;/p&gt;

&lt;p&gt;本文介绍如何实现在发生特定异常时中断，以便调查此时程序的状态的纯代码实现。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;第一次机会异常&quot;&gt;第一次机会异常&lt;/h2&gt;

&lt;p&gt;.NET 程序代码中的任何一段代码，在刚刚抛出异常，还没有被任何处理的那一时刻，AppDomain 的实例会引发一个 FirstChanceException 事件，用于通知此时刚刚开始发生了一个异常。&lt;/p&gt;

&lt;p&gt;于是我们可以通过监听第一次机会异常来获取到异常刚刚发生那一刻而还没有被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 的状态：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.ExceptionServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DoubiBlogs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FirstChanceException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 这里是程序的其他代码。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstChanceExceptionEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 在这里，可以通过 e.Exception 来获取到这个异常。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在第一次机会异常处中断&quot;&gt;在第一次机会异常处中断&lt;/h2&gt;

&lt;p&gt;我在这篇博客中举了一个例子来说明如何在发生异常的时候中断，不过是使用 Visual Studio：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/break-when-a-specific-exception-throw-in-visual-studio&quot;&gt;在 Visual Studio 中设置当发生某个特定异常或所有异常时中断&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么现在我们使用第一次机会异常来完善一下其中的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.ExceptionServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DoubiBlogs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FirstChanceException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\walterlv\逗比博客\不存在的文件.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;出现了异常&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstChanceExceptionEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 现在，我们使用 Debugger.Break() 来中断程序。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;保持 Visual Studio 异常设置窗格中的异常设置处于默认状态（意味着被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 的异常不会在 Visual Studio 中中断）。&lt;/p&gt;

&lt;p&gt;现在运行这个程序，你会发现程序发生了中断，在我们写下了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Break()&lt;/code&gt; 的那段代码上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-13-08-47.png&quot; alt=&quot;程序发生中断&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而在这个时候查看 Visual Studio 中程序的堆栈，可以发现其实调用堆栈是接在一开始发生异常的那一个方法的后面的，而且是除了非托管代码之外帧都是相邻的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-10-28-05.png&quot; alt=&quot;应用程序堆栈&quot; /&gt;&lt;/p&gt;

&lt;p&gt;双击 Visual Studio 堆栈中亮色的帧，即可定位到我们自己写的代码。因此，双击第一个亮色的帧可以转到我们自己写的代码中第一个引发异常的代码块。这个时候可以查看应用程序中各处的状态，这正好是发生此熠时的状态（而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 之后的状态）。&lt;/p&gt;

&lt;h2 id=&quot;优化代码和提示&quot;&gt;优化代码和提示&lt;/h2&gt;

&lt;p&gt;为了让这段代码包装得更加“魔性”，我们可以对第一次机会异常的事件加以处理。现在，我们这么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerStepThrough&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggerNonUserCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstChanceExceptionEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExceptionDebugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDebugger&lt;/code&gt; 类型如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DoubiBlogs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExceptionDebugger&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 现在请查看 Visual Studio 中的堆栈以迅速定位刚刚发生异常时的程序状态。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果你按下 F10，可以立刻但不跳转到你第一个出现异常的代码块中。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BreakCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;




        &lt;span class=&quot;c1&quot;&gt;// 现在请查看 Visual Studio 中的堆栈以迅速定位刚刚发生异常时的程序状态。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果你按下 F10，可以立刻但不跳转到你第一个出现异常的代码块中。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LaunchCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;




        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerStepThrough&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggerNonUserCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;BreakCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;LaunchCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，发生了第一次机会异常的时候，会断点在我们写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BreakCore&lt;/code&gt; 方法上。这里的代码很少，因此开发者看到这里的时候可以很容易地注意到上面的注释以了解到如何操作。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-13-18-59.png&quot; alt=&quot;自己设的断点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在再看堆栈，依然像前面一样，找到第一个亮色的帧可以找到第一个抛出异常的我们的代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-13-18-19.png&quot; alt=&quot;调用堆栈&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意，我们在从第一次机会异常到后面中断的代码中，都设置了这两个特性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerStepThrough&lt;/code&gt; 设置此属性可以让断点不会出现在写的这几个方法中
    &lt;ul&gt;
      &lt;li&gt;于是，当你按下 F10 的时候，会跳过所有标记了此特性的方法，这可以直接跳转到最终发生异常的那段代码中去。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerNonUserCode&lt;/code&gt; 设置此代码非用户编写的代码
    &lt;ul&gt;
      &lt;li&gt;于是，在 Visual Studio 的堆栈中，我们会发现这几个方法会变成暗色的，Visual Studio 不会优先显式这部分的源代码，这可以让错误在最关键的代码中显示而不会被我们刚刚写的这些代码中污染。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;附加调试器&quot;&gt;附加调试器&lt;/h2&gt;

&lt;p&gt;前面的代码中，我们做了一个判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.IsAttached&lt;/code&gt;。这是在判断，如果当前没有附加调试器，那么就附加一个。&lt;/p&gt;

&lt;p&gt;于是这段代码可以运行在非 Visual Studio 的环境中，当出现了异常的时候，还可以补救选择一个调试器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-13-25-12.png&quot; alt=&quot;附加调试器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，实际上附加到 Visual Studio 进行调试也是最佳的方法。只不过，我们不需要一定通过 Visual Studio，我们可以在一般测试代码的时候也能获得出现特定异常时立刻开始断点调查异常的特性。&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Jul 2019 05:26:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/set-a-breakpoint-when-exception-occurred.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/set-a-breakpoint-when-exception-occurred.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 中设置当发生某个特定异常或所有异常时中断</title>
        <description>&lt;p&gt;当使用 Visual Studio 调试的时候，如果我们的代码中出现了异常，那么 Visual Studio 会让我们的程序中断，然后我们就能知道程序中出现了异常。但是，如果这个异常已经被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 了，那么默认情况下 Visual Studio 是不会帮我们中断的。&lt;/p&gt;

&lt;p&gt;能否在这个异常发生的第一时间让 Visual Studio 中断程序以便于我们调试呢？本文将介绍方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;会中断的异常&quot;&gt;会中断的异常&lt;/h2&gt;

&lt;p&gt;看下面这一段代码，读取一个根本不存在的文件。我们都知道这会抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileNotFoundException&lt;/code&gt;，随后 Visual Studio 会中断，然后告诉我们这句话发生了异常。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DoubiBlogs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\walterlv\逗比博客\不存在的文件.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-28-21.png&quot; alt=&quot;Visual Studio 异常中断&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;不会中断的异常&quot;&gt;不会中断的异常&lt;/h2&gt;

&lt;p&gt;现在，我们为这段会出异常的代码加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DoubiBlogs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\walterlv\逗比博客\不存在的文件.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;出现了异常&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在再运行，会发现 Visual Studio 并没有在出现此异常的时候中断，而是完成了程序最终的输出，随后结束程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-30-22.png&quot; alt=&quot;程序正常结束，没有中断&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;设置发生所有异常时中断&quot;&gt;设置发生所有异常时中断&lt;/h2&gt;

&lt;p&gt;有时我们会发现已经 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 过的代码在后来也可能被证明有问题，于是希望即便被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 也要发生中断，以便在异常发生的第一时刻定位问题。&lt;/p&gt;

&lt;p&gt;Visual Studio 提供了一个异常窗格，可以用来设置在发生哪些异常的时候一定会中断并及时给出提示。&lt;/p&gt;

&lt;p&gt;异常窗格可以在“调试”-&amp;gt;“窗口”-&amp;gt;“异常设置”中打开：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-35-20.png&quot; alt=&quot;异常设置窗口的打开方法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在异常设置窗格中，我们可以将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Common Language Runtime Exceptions&lt;/code&gt; 选项打勾，这样任何 CLR 异常引发的时候 Visual Studio 都会中断而无论是否有 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块处理掉了此异常。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-39-13.png&quot; alt=&quot;将 CLR 异常打勾&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果需要恢复设置，点击上面的恢复成默认的按钮即可。&lt;/p&gt;

&lt;h2 id=&quot;设置发生特定异常时中断或不中断&quot;&gt;设置发生特定异常时中断或不中断&lt;/h2&gt;

&lt;p&gt;当然，你也可以不需要全部打勾，而是只勾选你期望诊断问题的那几个异常。你可以试试，这其实是一个非常繁琐的工作，你会在大量的异常名称中失去眼神而再也无法直视任何异常了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-41-44.png&quot; alt=&quot;只勾选期望诊断问题的几个异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以更推荐的做法不是仅设置特定异常时中断，而是反过来设置——设置发生所有异常时中断，除了特定的一些异常之外。&lt;/p&gt;

&lt;p&gt;方法是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将整个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Common Language Runtime Exceptions&lt;/code&gt; 打勾&lt;/li&gt;
  &lt;li&gt;在实际运行程序之后，如果发生了一些不感兴趣的异常，那么就在下面的框中将此异常取消勾选即可&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-04-09-45-06.png&quot; alt=&quot;设置发生此异常时中断&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;脱离-visual-studio-设置&quot;&gt;脱离 Visual Studio 设置&lt;/h2&gt;

&lt;p&gt;如果程序并不是在 Visual Studio 中运行，那么有没有方法进行中断呢？&lt;/p&gt;

&lt;p&gt;一个做法是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch()&lt;/code&gt;，但这样的话中断的地方就是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch()&lt;/code&gt; 所在的代码处，可能异常还没发生或者已经发生过了。&lt;/p&gt;

&lt;p&gt;有没有方法可以在异常发生的那一刻中断呢？请阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/set-a-breakpoint-when-exception-occurred&quot;&gt;.NET/C# 中设置当发生某个特定异常时进入断点（不借助 Visual Studio 的纯代码实现）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 04 Jul 2019 05:07:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/break-when-a-specific-exception-throw-in-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/break-when-a-specific-exception-throw-in-visual-studio.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>.NET/MSBuild 中的发布路径在哪里呢？如何在扩展编译的时候修改发布路径中的文件呢？</title>
        <description>&lt;p&gt;在扩展 MSBuild 编译的时候，我们一般的处理的路径都是临时路径或者输出路径，那么发布路径在哪里呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我曾经在下面这一篇博客中说到可以通过阅读 Microsoft.NET.Sdk 的源码来探索我们想得知的扩展编译的答案：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，我们可以搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;Publish&quot;&lt;/code&gt; 这样的关键字找到我们希望找到的编译目标，于是找到在 Microsoft.NET.Sdk.Publish.targets 文件中，有很多的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PublishDir&lt;/code&gt; 属性存在，这可以很大概率猜测这个就是发布路径。不过我只能在这个文件中找到这个路径的再次赋值，找不到初值。&lt;/p&gt;

&lt;p&gt;如果全 Sdk 查找，可以找到更多赋初值和使用它复制和生成文件的地方。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-19-52-15.png&quot; alt=&quot;PublishDir 全文查找&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是可以确认，这个就是最终的发布路径，只不过不同类型的项目，其发布路径都是不同的。&lt;/p&gt;

&lt;p&gt;比如默认是：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PublishDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(PublishDir)'==''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(OutputPath)app.publish\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PublishDir&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还有：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;_DeploymentApplicationDir&amp;gt;&lt;/span&gt;$(PublishDir)$(_DeploymentApplicationFolderName)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_DeploymentApplicationDir&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;和其他。&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Jul 2019 01:47:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/where-is-msbuild-publish-folder.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/where-is-msbuild-publish-folder.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>如何给 Windows Terminal 增加一个新的终端（以 Bash 为例）</title>
        <description>&lt;p&gt;Windows Terminal 的预览版本可以在微软应用商店下载，下载完后它原生就可以打开三个不同的终端 PowerShell Core、CMD 和 PowerShell。然而我的计算机上还安装了一个 Bash 可以如何添加到 Windows Terminal 里呢？&lt;/p&gt;

&lt;p&gt;本文将介绍添加一个新终端应该如何修改配置。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;下载安装-windows-terminal&quot;&gt;下载安装 Windows Terminal&lt;/h2&gt;

&lt;p&gt;Windows Terminal 预览版已上架微软应用商店，你可以前往下载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/store/productId/9N0DX20HK701&quot;&gt;https://www.microsoft.com/store/productId/9N0DX20HK701&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;随后，在开始菜单中启动 Windows Terminal。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-24-01.png&quot; alt=&quot;Windows Terminal&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;打开配置文件&quot;&gt;打开配置文件&lt;/h2&gt;

&lt;p&gt;在界面的右上角点按下拉按钮，点击“Settings”可以打开配置文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-23-22.png&quot; alt=&quot;Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个配置文件虽然看起来有 300+ 行，但实际上结构非常简单。我把它折叠起来加上一点点注释你应该很容易看出其配置文件的结构。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-29-08.png&quot; alt=&quot;配置文件的结构&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;新增一个-profile&quot;&gt;新增一个 profile&lt;/h2&gt;

&lt;p&gt;我们把原来的一个 profile 复制一份出来，这样我们就能够写一份自己的终端配置了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-31-12.png&quot; alt=&quot;新复制出来一个 profile&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面是我添加的 Bash 的配置。如果你是通过安装 Git for Windows 而安装的 Git Bash，那么默认路径就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\Git\bin\bash.exe&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;acrylicOpacity&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;closeOnExit&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;colorScheme&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Campbell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandline&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Program Files&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Git&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bash.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cursorColor&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#FFFFFF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cursorShape&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fontFace&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Monaco&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fontSize&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;guid&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{1d4e097e-fe87-4164-97d7-3ca794c316fd}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;historySize&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;icon&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;git-bash.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Bash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;padding&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0, 0, 0, 0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;snapOnInput&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;startingDirectory&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%USERPROFILE%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;useAcrylic&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，必须要改的有这些项：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;commandline&lt;/code&gt; 你需要改成你的新的终端的路径；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;guid&lt;/code&gt; 必须使用新的跟其他终端不重复的 guid；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; 改为终端的名称（本例中是 Bash，虽然不是必须，但强烈建议修改）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Visual Studio 自带了一个 guid 生成工具，你可以在菜单的工具中找到：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-36-12.png&quot; alt=&quot;Visual Studio 自带的 GUID 生成工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你也可以在网上搜索 GUID 生成器得到很多在线的 GUID 生成工具。&lt;/p&gt;

&lt;p&gt;另外，还有一些可选的参数：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;useAcrylic&lt;/code&gt; 使用亚克力效果&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;acrylicOpacity&lt;/code&gt; 亚克力效果透明度&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;colorScheme&lt;/code&gt; 配色方案（配置文件后面自带了五种配色方案，你也可以额外再添加新的配色方案）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fontFace&lt;/code&gt; 字体名称&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fontSize&lt;/code&gt; 字号大小&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;icon&lt;/code&gt; 图标&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;startingDirectory&lt;/code&gt; 初始路径&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，你可能需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;icon&lt;/code&gt; 文件，下面有一个 Git Bash 的图标，有需要自取：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-10-22-00.png&quot; alt=&quot;Git Bash 图标&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;最终效果&quot;&gt;最终效果&lt;/h2&gt;

&lt;p&gt;在你按下 Ctrl+S 保存这个配置文件之后，配置将会立刻生效。你可以在你的 Windows Terminal 中看到你新增的 Bash 终端了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-03-16-51-11.png&quot; alt=&quot;最终效果&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 03 Jul 2019 08:51:53 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-a-new-profile-for-windows-terminal.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-a-new-profile-for-windows-terminal.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; node starts to support the &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; attribute since MSBuild release the 15.0 version which is embedded in Visual Studio 2017. For the &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; attribute, the C# project file whose file extension is csproj becomes much more powerful and extensible.&lt;/p&gt;

&lt;p&gt;We’ll try to read the source code of &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; which is the default Sdk value of C#.NET project and try to write some creative extension of compiling.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/read-microsoft-net-sdk.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/read-microsoft-net-sdk-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;where-to-find-the-source-code-of-microsoftnetsdk&quot;&gt;Where to find the source code of Microsoft.NET.Sdk&lt;/h2&gt;

&lt;p&gt;Search &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; using &lt;a href=&quot;https://www.voidtools.com/&quot;&gt;Everything&lt;/a&gt; or &lt;a href=&quot;https://github.com/Wox-launcher/Wox&quot;&gt;Wox&lt;/a&gt;, I find that multiple versions are installed in my computer. As I’ve installed the .NET Core 2.1, the location of my latest version is &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\dotnet\sdk\3.0.100-preview6-012264&lt;/code&gt;. The official document &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK&lt;/a&gt; says that if you implement your own Sdk, you can also push it to &lt;nuget.org&gt;.&lt;/nuget.org&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-06-06.png&quot; alt=&quot;Search Microsoft.NET.Sdk&quot; /&gt;&lt;br /&gt;
▲ Search Microsoft.NET.Sdk&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-08-25.png&quot; alt=&quot;The Sdk folder&quot; /&gt;&lt;br /&gt;
▲ The Sdk folder on my computer&lt;/p&gt;

&lt;p&gt;The NuGet part of &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; is on GitHub:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/NuGet.Client/tree/dev/src/NuGet.Core&quot;&gt;NuGet.Client/src/NuGet.Core at dev · NuGet/NuGet.Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-folder-structure-of-microsoftnetsdk&quot;&gt;The folder structure of Microsoft.NET.Sdk&lt;/h2&gt;

&lt;p&gt;When clicking into the &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; folder, we can find that the folder structure is very similar to the NuGet folder structure.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-09-29.png&quot; alt=&quot;The folder structure of Microsoft.NET.Sdk&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’ve written some posts talking about the NuGet folder structure but unfortunately they are all not in English:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;How to Write a Cross-Platform NuGet Tool Package Base on MSBuild Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;How to Write a Cross-Platform NuGet Tool Package Base on Command Line Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft have some official document talking about the NuGet folder structure &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#from-a-convention-based-working-directory?wt.mc_id=MVP&quot;&gt;How to create a NuGet package from a convention-based working directory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; there exists an extra &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; folder for the &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; kind of NuGet package.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-10-19.png&quot; alt=&quot;The extra Sdk folder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk.props&lt;/code&gt; file and the &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdj.targets&lt;/code&gt; file will be imported by default and Microsoft’s official document also mentions it here: &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK - Visual Studio&lt;/a&gt;. It says that the two code blocks are actually the same:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net46&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Implicit top import --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sdk.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net46&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Implicit bottom import --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sdk.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because of the default importation behavior, Sdk can do variaty of tasks when MSBuild or Roslyn build the .NET projects. The default Sdk &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; is very extensible so that we can easily use it to customize out compiling behavior and I’ve mentioned these in the two non-English posts above.&lt;/p&gt;

&lt;h2 id=&quot;the-major-targets-of-microsoftnetsdk&quot;&gt;The major targets of Microsoft.NET.Sdk&lt;/h2&gt;

&lt;p&gt;I try to search &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; node in the whole &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; folder and find out 174 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;s. Don’t worry about the huge &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; amount because most of them are private by indicating the name with a &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; prefix and some of them have the same name to override and wait to be overridden.&lt;/p&gt;

&lt;p&gt;So the core compiling &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; is not so many and I pick up some core &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CollectPackageReferences&lt;/code&gt;: Collect the &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; items to resolve the package dependencies of the project.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; The core compiling &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateAssemblyInfo&lt;/code&gt;: Generate the &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyInfo.cs&lt;/code&gt; file which is usually written by developers manually before .NET Core published.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Pack&lt;/code&gt;: Pack current project into a NuGet package file whose extension is nupkg.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateNuspec&lt;/code&gt;: Generate the nuspec file which is the meta info of the NuGet package.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;write-creative-extensions-of-compiling&quot;&gt;Write creative extensions of compiling&lt;/h2&gt;

&lt;p&gt;I also find some creative &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; that inspires me:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DontRestore&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Restore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This project should not be restored&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ If a &lt;code class=&quot;highlighter-rouge&quot;&gt;Restore&lt;/code&gt; target exists, then report a compiling error.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceStaticLegacyPackage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectPackageReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ This is written by me to prevent a specific package named &lt;code class=&quot;highlighter-rouge&quot;&gt;LiteDB&lt;/code&gt; to be upgrade. I post this method in &lt;a href=&quot;/post/prevent-nuget-package-upgrade&quot;&gt;my another non-English post&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 02 Jul 2019 11:43:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/read-microsoft-net-sdk-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/read-microsoft-net-sdk-en.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程</title>
        <description>&lt;p&gt;在 csproj 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 属性是 MSBuild 15.0 开始支持的，也就是 Visual Studio 2017 开始支持。有了 Sdk 属性的存在，MSBuild 编译过程能够扩展得非常强大，而不止是过去 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 的一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;targets&lt;/code&gt; 文件。&lt;/p&gt;

&lt;p&gt;本文将介绍 Microsoft.NET.Sdk 的源码，以及利用源码中的一些线索来完成官方文档中没有提及的功能扩展。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/read-microsoft-net-sdk.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/read-microsoft-net-sdk-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;microsoftnetsdk-源码的位置&quot;&gt;Microsoft.NET.Sdk 源码的位置&lt;/h2&gt;

&lt;p&gt;在计算机上全局搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 可以找到不同版本的多个 Sdk 目录，由于我安装了 .NET Core 3.0，所以找到的目录是：&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\dotnet\sdk\3.0.100-preview6-012264&lt;/code&gt;。当然，按照官网 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK&lt;/a&gt; 的描述，如果自己实现了一套 Sdk，也可以以 NuGet 包的形式发布。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-06-06.png&quot; alt=&quot;Search Microsoft.NET.Sdk&quot; /&gt;&lt;br /&gt;
▲ 搜索 Microsoft.NET.Sdk&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-08-25.png&quot; alt=&quot;The Sdk folder&quot; /&gt;&lt;br /&gt;
▲ 我计算机上的 Sdk 文件夹&lt;/p&gt;

&lt;p&gt;Sdk 中的 NuGet 部分在 GitHub 上的仓库地址：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/NuGet.Client/tree/dev/src/NuGet.Core&quot;&gt;NuGet.Client/src/NuGet.Core at dev · NuGet/NuGet.Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;microsoftnetsdk-的目录结构&quot;&gt;Microsoft.NET.Sdk 的目录结构&lt;/h2&gt;

&lt;p&gt;在打开看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 的目录结构后，我们可以发现这几乎就是 NuGet 包要求的目录结构。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-09-29.png&quot; alt=&quot;The folder structure of Microsoft.NET.Sdk&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于 NuGet 包的目录结构，我在下面两篇文章中都有提到过：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;官方对 NuGet 的目录结构也有介绍：&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#from-a-convention-based-working-directory?wt.mc_id=MVP&quot;&gt;How to create a NuGet package from a convention-based working directory&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;不过，Sdk 类型的 NuGet 包会多一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 文件夹。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-21-10-19.png&quot; alt=&quot;The extra Sdk folder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 文件夹中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk.targets&lt;/code&gt; 是会被默认 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 的，这一点在官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK - Visual Studio&lt;/a&gt; 中是有说明的，以下两段代码的含义相同：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Implicit top import --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sdk.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Implicit bottom import --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sdk.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;由于这两个文件的默认引入，Sdk 可以完成非常多的编译任务。而且通常 Sdk 带有扩展性，使得我们可以很方便地对项目的编译过程进行扩展，这一点在我前面提到了两篇制作 NuGet 工具包的文章中都有说明。&lt;/p&gt;

&lt;h2 id=&quot;microsoftnetsdk-的主要任务&quot;&gt;Microsoft.NET.Sdk 的主要任务&lt;/h2&gt;

&lt;p&gt;在 Sdk 文件夹中搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点的个数，我得到了 174 个（随 .NET Core 2.1 发布）；不过有一些是同名的，会被重写（类似于 C#/.NET 中的继承和重写）；核心的并没有那么多。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CollectPackageReferences&lt;/code&gt; 用于收集 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 收集到的所有依赖（也就是 NuGet 包依赖）&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 核心的编译过程&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateAssemblyInfo&lt;/code&gt; 用于生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyInfo.cs&lt;/code&gt; 文件（以前可是手工写的呢）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Pack&lt;/code&gt; 用于将当前程序集打包成一个 NuGet 包&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateNuspec&lt;/code&gt; 在打包之前生成 nuspec 文件&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;定制富有创意的编译过程&quot;&gt;定制富有创意的编译过程&lt;/h2&gt;

&lt;p&gt;下面是 Microsoft.NET.Sdk 中发现的一些富有创意的编译过程：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DontRestore&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Restore&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This project should not be restored&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 如果有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Restore&lt;/code&gt;，那么让你编译不通过&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceStaticLegacyPackage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectPackageReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 这是我另外写的一篇文章：&lt;a href=&quot;/post/prevent-nuget-package-upgrade&quot;&gt;阻止某个 NuGet 包意外升级&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?wt.mc_id=MVP&quot;&gt;How to: Reference an MSBuild Project SDK - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 02 Jul 2019 11:42:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/read-microsoft-net-sdk.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/read-microsoft-net-sdk.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>App will crash when using the when keyword in a catch expression</title>
        <description>&lt;p&gt;We know that we can add a &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; keyword after a &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; filter. But if there is another exception happened in the &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; expression, the app will totally crash.&lt;/p&gt;

&lt;p&gt;This happens in .NET Framework 4.8 but in .NET Core 3.0, it works correctly as the document says.&lt;/p&gt;

&lt;p&gt;Maybe this is a bug in the .NET Framework 4.8 CLR.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文使用 &lt;strong&gt;多种语言&lt;/strong&gt; 编写，请选择你想阅读的语言：&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/try-catch-when-causes-app-crash.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/try-catch-when-causes-app-crash-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-when-in-the-official-document&quot;&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; in the official document&lt;/h2&gt;

&lt;p&gt;You can view the official document here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/exceptions/using-user-filtered-exception-handlers&quot;&gt;Using User-Filtered Exception Handlers - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is such a sentence here:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The expression of the user-filtered clause is not restricted in any way. If an exception occurs during execution of the user-filtered expression, that exception is discarded and the filter expression is considered to have evaluated to false. In this case, the common language runtime continues the search for a handler for the current exception.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When there is an exception occurred in the when expression the exception will be ignored and the expression will return false.&lt;/p&gt;

&lt;h2 id=&quot;a-demo&quot;&gt;A demo&lt;/h2&gt;

&lt;p&gt;We can write a demo to verify this behavior of the official document.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.CatchWhenCrash&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Try&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catch 3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;End&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Obviously, the &lt;code class=&quot;highlighter-rouge&quot;&gt;FileName&lt;/code&gt; property will keep &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; in the first when expression and will cause a &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;. It is not recommended to write such the code but it can help us verify the behavior of the &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; blocks.&lt;/p&gt;

&lt;p&gt;If the official document is correct then we can get the output as &lt;code class=&quot;highlighter-rouge&quot;&gt;Try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;Catch 2&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt; because the exception in the &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; will be ignored and the outer &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; will not catch it and then the &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt; expression returns false so that the exception handling goes into the second one.&lt;/p&gt;

&lt;h2 id=&quot;in-net-core-30-and-in-net-framework-48&quot;&gt;In .NET Core 3.0 and in .NET Framework 4.8&lt;/h2&gt;

&lt;p&gt;The pictures below show the actual output of the demo code above in .NET Core 3.0 and in .NET Framework 4.8.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-06-35.png&quot; alt=&quot;.NET Core 3.0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-08-21.png&quot; alt=&quot;.NET Framework 4.8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Only in the .NET Core 3.0, the output behaves the same as the official document says. But in .NET Framework 4.8, the &lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt; even not appear in the output. We can definitely sure that the app crashes in .NET Framework 4.8.&lt;/p&gt;

&lt;p&gt;If we run the app step by step in Visual Studio, we can see that a CLR exception happens.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-15-10-46.png&quot; alt=&quot;CLR error&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This animated picture below shows how the code goes step by step.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-07-02-catch-when-crash.gif&quot; alt=&quot;Step debugging&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Jul 2019 11:32:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/try-catch-when-causes-app-crash-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/try-catch-when-causes-app-crash-en.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets？</title>
        <description>&lt;p&gt;在为 .NET 项目扩展 MSBuild 编译而编写编译目标（Target）时，我们会遇到用于扩展编译目标用的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这三个应该分别在什么情况下用呢？本文将介绍其用法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;beforetargets--aftertargets&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 是用来扩展编译用的。&lt;/p&gt;

&lt;p&gt;如果你希望在某个编译任务开始执行一定要执行你的编译目标，那么请使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt;。例如我想多添加一个文件加入编译，那么写：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvIncludeSourceFiles&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileFullPath)..\src\Foo.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，一个 Foo.cs 就会在编译时加入到被编译的文件列表中，里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类就可以被使用了。这也是 NuGet 源代码包的核心原理部分。关于 NuGet 源代码包的制作方法，可以扩展阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/build-source-code-package-for-wpf-projects&quot;&gt;从零开始制作 NuGet 源代码包（全面支持 .NET Core / .NET Framework / WPF 项目）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你希望一旦执行完某个编译任务之后执行某个操作，那么请使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt;。例如我想在编译完成生成了输出文件之后，将这些输出文件拷贝到另一个调试目录，那么写：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CopyOutputLibToFastDebug&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputFileToCopy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)$(AssemblyName).dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/OutputFileToCopy&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputFileToCopy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)$(AssemblyName).pdb&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/OutputFileToCopy&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(OutputFileToCopy)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MainProjectPath)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/Copy&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种写法可以进行快速的组件调试。下面这篇博客就是用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 带来的此机制来实现的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E8%AE%A9-visualstudio-%E6%80%A5%E9%80%9F%E8%B0%83%E8%AF%95%E5%BA%95%E5%B1%82%E5%BA%93%E6%96%B9%E6%B3%95&quot;&gt;Roslyn 让 VisualStudio 急速调试底层库方法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 中写了多个 Target 的名称（用分号分隔），那么只要任何一个准备执行或者执行完毕，就会触发此 Target 的执行。&lt;/p&gt;

&lt;h2 id=&quot;dependsontargets&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 是用来指定依赖的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 并不会直接帮助你扩展一个编译目标，也就是说如果你只为你的 Target 写了一个名字，然后添加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 属性，那么你的 Target 在编译期间根本都不会执行。&lt;/p&gt;

&lt;p&gt;但是，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt;，你可以更好地控制执行流程和其依赖关系。&lt;/p&gt;

&lt;p&gt;例如上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyOutputLibToFastDebug&lt;/code&gt; 这个将输出文件复制到另一个目录的编译目标（Target），依赖于一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainProjectPath&lt;/code&gt; 属性，因此计算这个属性值的编译目标（Target）应该设成此 Target 的依赖。&lt;/p&gt;

&lt;p&gt;当 A 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;B;C;D&lt;/code&gt; 时，那么一旦准备执行 A 时将会发生：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果 B C D 中任何一个曾经已经执行过，那么就忽略（因为已经执行过了）&lt;/li&gt;
  &lt;li&gt;如果 B C D 中还有没有执行的，就立刻执行&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;实践&quot;&gt;实践&lt;/h2&gt;

&lt;p&gt;当我们实际上在扩展编译的时候，我们会用到不止一个编译目标，因此这几个属性都是混合使用的。但是，你应该在合适的地方编写合适的属性设置。&lt;/p&gt;

&lt;p&gt;例如我们做一个 NuGet 包，这个 NuGet 包的 .targets 文件中写了下面几个 Target：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;_WalterlvEvaluateProperties
    &lt;ul&gt;
      &lt;li&gt;用于初始化一些属性和参数，其他所有的 Target 都依赖于这些参数&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;_WalterlvGenerateStartupObject
    &lt;ul&gt;
      &lt;li&gt;生成一个类，包含 Main 入口点函数，然后将入口点设置成这个类&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;_WalterlvIncludeSourceFiles
    &lt;ul&gt;
      &lt;li&gt;为目标项目添加一些源代码，这就包含刚刚新生成的入口点类&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;_WalterlvPackOutput
    &lt;ul&gt;
      &lt;li&gt;将目标项目中生成的文件进行自定义打包&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;那么我们改如何为每一个 Target 设置正确的属性呢？&lt;/p&gt;

&lt;p&gt;第一步：找出哪些编译目标是真正完成编译任务的，这些编译目标需要通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTarget&lt;/code&gt; 设置扩展编译。&lt;/p&gt;

&lt;p&gt;于是我们可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvPackOutput&lt;/code&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt; 需要添加参与编译的源代码文件，因此我们需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvPackOutput&lt;/code&gt; 需要在编译完成后进行自定义打包，因此我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt;。这个时候可以确保文件已经生成完毕可以使用了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第二步：找到依赖关系，这些依赖关系需要通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 来执行。&lt;/p&gt;

&lt;p&gt;于是我们可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvEvaluateProperties&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvGenerateStartupObject&lt;/code&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvEvaluateProperties&lt;/code&gt; 被其他所有的编译目标使用了，因此，我们需要将后面所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 属性设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvEvaluateProperties&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvGenerateStartupObject&lt;/code&gt; 生成的入口点函数被 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt; 加入到编译中，因此 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 属性需要添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvGenerateStartupObject&lt;/code&gt;（添加方法是使用分号“;”分隔）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;将所有的这些编译任务合在一起写，将是下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvGenerateStartupObject&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvIncludeSourceFiles&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties;_WalterlvGenerateStartupObject&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvPackOutput&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;具体依赖于抽象&quot;&gt;具体依赖于抽象&lt;/h2&gt;

&lt;p&gt;我们平时在编写代码时会考虑面向对象的六个原则，其中有一个是依赖倒置原则，即具体依赖于抽象。&lt;/p&gt;

&lt;p&gt;你不这么写代码当然不会带来错误，但会带来维护性困难。在编写扩展编译目标的时候，这一条同样适用。&lt;/p&gt;

&lt;p&gt;假如我们要写的编译目标不止上面这些，还有更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;_WalterlvConvertTemplateCompileToRealCompile
    &lt;ul&gt;
      &lt;li&gt;包里有一些模板代码，会在编译期间转换为真实代码并加入编译&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;_WalterlvConditionalImportedSourceCode
    &lt;ul&gt;
      &lt;li&gt;会根据 NuGet 包用户的设置有条件地引入一些额外的源代码&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么这个时候我们前面写的用于引入源代码的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt; 编译目标其依赖的 Target 会更多。似乎看起来应该这么写了：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvIncludeSourceFiles&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties;_WalterlvGenerateStartupObject;_WalterlvConvertTemplateCompileToRealCompile;_WalterlvConditionalImportedSourceCode&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但你小心：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这个列表会越来越长，而且指不定还会增加一些边边角角的引入的新的源代码呢&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvConditionalImportedSourceCode&lt;/code&gt; 是有条件的，而我们 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 这样的写法会导致这个 Target 的条件失效&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里更抽象的编译目标是 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFiles&lt;/code&gt;，我们的依赖关系倒置了！&lt;/p&gt;

&lt;p&gt;为了解决这样的问题，我们引入一个新的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvIncludeSourceFilesDependsOn&lt;/code&gt;，如果有编译目标在编译过程中生成了新的源代码，那么就需要将自己加入到此属性中。&lt;/p&gt;

&lt;p&gt;现在的源代码看起来是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里是一个文件 --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
    $(_WalterlvIncludeSourceFilesDependsOn);
    _WalterlvGenerateStartupObject
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvGenerateStartupObject&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvIncludeSourceFiles&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_WalterlvIncludeSourceFilesDependsOn)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvPackOutput&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里是另一个文件 --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
    $(_WalterlvIncludeSourceFilesDependsOn);
    _WalterlvConvertTemplateCompileToRealCompile;
    _WalterlvConditionalImportedSourceCode
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(UseWalterlvDemoCode)' == 'True' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
    $(_WalterlvIncludeSourceFilesDependsOn);
    _WalterlvConditionalImportedSourceCode
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvIncludeSourceFilesDependsOn&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvConvertTemplateCompileToRealCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvConditionalImportedSourceCode&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(UseWalterlvDemoCode)' == 'True' &quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvEvaluateProperties&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上，Microsoft.NET.Sdk 内部有很多的编译任务是通过这种方式提供的扩展，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;BuildDependsOn&lt;/li&gt;
  &lt;li&gt;CleanDependsOn&lt;/li&gt;
  &lt;li&gt;CompileDependsOn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你可以阅读我的另一篇博客了解更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/extend-the-visual-studio-build-process&quot;&gt;通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 01 Jul 2019 08:43:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-before-after-targets-vs-depends-on-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-before-after-targets-vs-depends-on-targets.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 Visual Studio 编译时，让错误一开始发生时就停止编译（以便及早排查编译错误节省时间）</title>
        <description>&lt;p&gt;对于稍微大一点的 .NET 解决方案来说，编译时间通常都会长一些。如果项目结构和差量编译优化的好，可能编译完也就 5~30 秒，但如果没有优化好，那么出现 1~3 分钟都是可能的。&lt;/p&gt;

&lt;p&gt;如果能够在编译出错的第一时间停止编译，那么我们能够更快地去找编译错误的原因，也能从更少的编译错误列表中找到出错的关键原因。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你只是觉得你的项目或解决方案编译很慢而不知道原因，我推荐你安装 Parallel Builds Monitor 插件来调查一下。你可以阅读我的一篇博客来了解它：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/visual-studio-extension-parallel-builds-monitor&quot;&gt;Visual Studio 使用 Parallel Builds Monitor 插件迅速找出编译速度慢的瓶颈，优化编译速度 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一个优化比较差的解决方案可能是下面这个样子的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-30-13-28-01.png&quot; alt=&quot;优化比较差的解决方案的编译甘特图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;明明没有多少个项目，但是项目之间的依赖几乎是一条直线，于是不可能开启项目的并行编译。&lt;/p&gt;

&lt;p&gt;图中这个项目的编译时长有 1 分 30 秒。可想而知，如果你的改动导致非常靠前的项目编译错误，而默认情况下编译的时候会继续尝试编译下去，于是你需要花非常长的时间才能等待编译完毕，然后从一大堆项目中出现的编译错误中找到最开始出现错误的那个（通常也是编译失败的本质原因）。&lt;/p&gt;

&lt;p&gt;现在，推荐使用插件 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=MikeWard-AnnArbor.VSColorOutput&quot;&gt;VSColorOutput&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;它的主要功能是给你的输出窗格加上颜色，可以让你更快速地区分调试信息、输出、警告和错误。&lt;/p&gt;

&lt;p&gt;不过，也正是因为它是通过匹配输出来上色的，于是它可以得知你的项目出现了编译错误，可以采取措施。&lt;/p&gt;

&lt;p&gt;在你安装了这款插件之后，你可以在 Visual Studio 的“工具”-&amp;gt;“设置”中找到 VSColorOutput 的设置。其中有一项是“Stop Build on First Error”，打开之后，再出现了错误的话，将第一时间会停止。你也可以发现你的 Visual Studio 错误列表中的错误数量非常少了，这些错误都是导致编译失败的最早出现的错误，利于你定位问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-30-13-32-45.png&quot; alt=&quot;VSColorOutput 的设置&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 30 Jun 2019 05:36:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/cancel-building-if-error-occurred-in-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/cancel-building-if-error-occurred-in-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 移动或重命名一个文件夹（如果存在，则合并而不是出现异常报错）</title>
        <description>&lt;p&gt;.NET 提供了一个简单的 API 来移动一个文件夹 &lt;code class=&quot;highlighter-rouge&quot;&gt;Directory.Move(string sourceDirName, string destDirName)&lt;/code&gt;。不过如果你稍微尝试一下这个 API 就会发现其实相当不实用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Directory.Move(string sourceDirName, string destDirName)&lt;/code&gt; 这个 API 来移动文件夹的时候，比如我们需要将 A 文件夹移动成 B 文件夹（也可以理解成重命名成 B）。&lt;/p&gt;

&lt;p&gt;一旦 B 文件夹是存在的，那么这个时候会抛出异常。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-22-42-34.png&quot; alt=&quot;抛出了异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而实际上我们可能希望这两个文件夹能够合并。&lt;/p&gt;

&lt;p&gt;.NET 的 API 没有原生提供合并两个文件夹的方法，所以我们需要自己实现。&lt;/p&gt;

&lt;p&gt;方法是递归遍历里面的所有文件，然后将源文件夹中的文件依次移动到目标文件夹中。为了应对复杂的文件夹层次结构，我写的方法中也包含了递归。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;MoveDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceFolder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DirectoryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumerateFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SearchOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopDirectoryOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumerateDirectories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SearchOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopDirectoryOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;back&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\\&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;MoveDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directoryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;depth&lt;/code&gt; 是一个整型，表示递归深度。我在计算文件需要移动到的新文件夹的路径的时候，需要使用到这个递归深度，以便回溯到最开始需要移动的那个文件夹上。&lt;/p&gt;
</description>
        <pubDate>Sat, 29 Jun 2019 04:57:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/move-files-from-a-directory-to-another.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/move-files-from-a-directory-to-another.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来</title>
        <description>&lt;p&gt;我们有很多的调试工具可以帮助我们查看 WPF 窗口中当前获得键盘焦点的元素。本文介绍监控当前键盘焦点元素的方法，并且提供一个不需要任何调试工具的自己绘制键盘焦点元素的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用调试工具查看当前获得键盘焦点的元素&quot;&gt;使用调试工具查看当前获得键盘焦点的元素&lt;/h2&gt;

&lt;p&gt;Visual Studio 带有实时可视化树的功能，使用此功能调试 WPF 程序的 UI 非常方便。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-28-08-58-54.png&quot; alt=&quot;打开实时可视化树&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在打开实时可视化树后，我们可以略微认识一下这里的几个常用按钮：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-28-09-03-11.png&quot; alt=&quot;实时可视化树中的常用按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里，我们需要打开两个按钮：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;为当前选中的元素显示外框&lt;/li&gt;
  &lt;li&gt;追踪具有焦点的元素&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样，只要你的应用程序当前获得焦点的元素发生了变化，就会有一个表示这个元素所在位置和边距的叠加层显示在窗口之上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-28-live-visual-tree-track-focused-element.gif&quot; alt=&quot;实时可视化树中的焦点追踪&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可能已经注意到了，Visual Studio 附带的这一叠加层会导致鼠标无法穿透操作真正具有焦点的元素。这显然不能让这一功能一直打开使用，这是非常不方便的。&lt;/p&gt;

&lt;h2 id=&quot;使用代码查看当前获得键盘焦点的元素&quot;&gt;使用代码查看当前获得键盘焦点的元素&lt;/h2&gt;

&lt;p&gt;我们打算在代码中编写追踪焦点的逻辑。这可以规避 Visual Studio 中叠加层中的一些问题，同时还可以在任何环境下使用，而不用担心有没有装 Visual Studio。&lt;/p&gt;

&lt;p&gt;获取当前获得键盘焦点的元素：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;focusedElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FocusedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过只是拿到这个值并没有多少意义，我们需要：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;能够实时刷新这个值；&lt;/li&gt;
  &lt;li&gt;能够将这个控件在界面上显示出来。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;实时刷新&quot;&gt;实时刷新&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Keyboard&lt;/code&gt; 有路由事件可以监听，得知元素已获得键盘焦点。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddGotKeyboardFocusHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xxx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;xxx&lt;/code&gt; 需要替换成监听键盘焦点的根元素。实际上，对于窗口来说，这个根元素可以唯一确定，就是窗口的根元素。于是我可以写一个辅助方法，用于找到这个窗口的根元素：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 用于存储当前已经获取过的窗口根元素。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 获取当前窗口的根元素。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindRootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 一个辅助方法，用于根据某个元素为起点查找当前窗口的根元素。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindRootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，监听键盘焦点的代码就可以变成：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddGotKeyboardFocusHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyboardFocusChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewFocus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在这里可以输出或者显示这个获得了键盘焦点的元素。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;显示&quot;&gt;显示&lt;/h3&gt;

&lt;p&gt;为了显示一个跟踪焦点的控件，我写了一个 UserControl，里面的主要代码是：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IsHitTestVisible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FocusBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderBrush=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#80159f5c&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Top&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;IsHitTestVisible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SnapsToDevicePixels=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OffsetBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#80159f5c&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-200 -4 -200 -4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Padding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12 0&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bottom&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;SnapsToDevicePixels=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OffsetTransform&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Y=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border.RenderTransform&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FocusDescriptionTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KeyboardFocusView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserControl&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;KeyboardFocusView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Unloaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnUnloaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FocusedElement&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetFocusVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddGotKeyboardFocusHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnUnloaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveGotKeyboardFocusHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGotFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyboardFocusChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewFocus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetFocusVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetFocusVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TranslatePoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TranslatePoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isOnTop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isOnBottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FocusBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FocusBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FocusBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FocusBorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;FocusDescriptionTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindRootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindRootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，只要将这个控件放到窗口中，这个控件就会一直跟踪窗口中的当前获得了键盘焦点的元素。当然，为了最好的显示效果，你需要将这个控件放到最顶层。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-28-focused-element.gif&quot; alt=&quot;实时可视化树中的焦点追踪&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;绘制并实时显示-wpf-程序中当前键盘焦点的元素&quot;&gt;绘制并实时显示 WPF 程序中当前键盘焦点的元素&lt;/h2&gt;

&lt;p&gt;如果我们需要监听应用程序中所有窗口中的当前获得键盘焦点的元素怎么办呢？我们需要给所有当前激活的窗口监听 &lt;code class=&quot;highlighter-rouge&quot;&gt;GotKeyboardFocus&lt;/code&gt; 事件。&lt;/p&gt;

&lt;p&gt;于是，你需要我在另一篇博客中写的方法来监视整个 WPF 应用程序中的所有窗口：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-monitor-all-windows-of-wpf-application&quot;&gt;如何监视 WPF 中的所有窗口，在所有窗口中订阅事件或者附加 UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;里面有一段对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationWindowMonitor&lt;/code&gt; 类的使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ApplicationWindowMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActiveWindowChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActiveWindowEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一旦有一个新的获得焦点的窗口出现，就可以在这里执行一些代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，我们只需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnActiveWindowChanged&lt;/code&gt; 事件中，将我面前面写的控件 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyboardFocusView&lt;/code&gt; 从原来的窗口中移除，然后放到新的窗口中即可监视新的窗口中的键盘焦点。&lt;/p&gt;

&lt;p&gt;由于每一次的窗口激活状态的切换都会更新当前激活的窗口，所以，我们可以监听整个 WPF 应用程序中所有窗口中的键盘焦点。&lt;/p&gt;
</description>
        <pubDate>Sat, 29 Jun 2019 01:07:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-track-wpf-focused-element.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-track-wpf-focused-element.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何监视 WPF 中的所有窗口，在所有窗口中订阅事件或者附加 UI</title>
        <description>&lt;p&gt;由于 WPF 路由事件（主要是隧道和冒泡）的存在，我们很容易能够通过只监听窗口中的某些事件使得整个窗口中所有控件发生的事件都被监听到。然而，如果我们希望监听的是整个应用程序中所有的事件呢？路由事件的路由可并不会跨越窗口边界呀？&lt;/p&gt;

&lt;p&gt;本文将介绍我编写的应用程序窗口监视器，来监听整个应用程序中所有窗口中的路由事件。这样的方法可以用来无时无刻监视 WPF 程序的各种状态。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;其实问题依旧摆在那里，因为我们依然无法让路由事件跨越窗口边界。更麻烦的是，我们甚至不知道应用程序有哪些窗口，这些窗口都是什么时机显示出来的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类中有一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows&lt;/code&gt;，这是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowCollection&lt;/code&gt; 类型的属性，可以用来获取当前已经被 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类管理的所有的窗口的集合。当然 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类内部还有一个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;NonAppWindowsInternal&lt;/code&gt; 用来管理与此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 没有逻辑关系的窗口集合。&lt;/p&gt;

&lt;p&gt;于是，我们只需要遍历 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows&lt;/code&gt; 集合便可以获得应用程序中的所有窗口，然后对每一个窗口监听需要的路由事件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里监听窗口中的事件。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等等！这种操作意味着将来新打开的窗口是不会被监听到事件的。&lt;/p&gt;

&lt;p&gt;我们有没有方法拿到新窗口的显示事件呢？遗憾的是——并不行。&lt;/p&gt;

&lt;p&gt;但是，我们有一些变相的处理思路。比如，由于 Windows 系统的特性，整个用户空间内，统一时刻只能有一个窗口能处于激活状态。我们可以利用当前窗口的激活与非激活的切换时机再去寻找新的窗口。&lt;/p&gt;

&lt;p&gt;于是，一开始的时候，我们可以监听一些窗口的激活事件。如果执行这段初始化代码的时候没有任何窗口是激活的状态，那么就监听所有窗口的激活事件；如果有一个窗口是激活的，那么就监听这个窗口的取消激活事件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitializeActivation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsActive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;UpdateActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 当前激活的窗口已经发生了改变，可以在这里为新的窗口做一些事情了。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window_Activated&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window_Deactivated&lt;/code&gt; 事件中，我们主要也是在做初始化。&lt;/p&gt;

&lt;p&gt;现在思路基本上全部清晰了，于是我将我写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationWindowMonitor&lt;/code&gt; 类的全部源码贴出来。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationWindowMonitor&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_windowFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_lastActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ApplicationWindowMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_windowFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InitializeActivation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitializeActivation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FilterWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsActive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;UpdateActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;UpdateActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FilterWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;availableWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deactivated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Deactivated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Activated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window_Activated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_lastActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lastActiveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_lastActiveWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FilterWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_windowFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_windowFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActiveWindowEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ActiveWindowEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用方法是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ApplicationWindowMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActiveWindowChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnActiveWindowChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActiveWindowEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一旦有一个新的获得焦点的窗口出现，就可以在这里执行一些代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationWindowMonitor&lt;/code&gt; 的构造函数中加入了一个过滤窗口的委托。比如你可以让窗口的监听只对主要的几个窗口生效，而对一些信息提示窗口忽略等等。&lt;/p&gt;
</description>
        <pubDate>Fri, 28 Jun 2019 00:38:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-monitor-all-windows-of-wpf-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-monitor-all-windows-of-wpf-application.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 如何获取当前进程的 CPU 和内存占用？如何获取全局 CPU 和内存占用？</title>
        <description>&lt;p&gt;都知道可以在任务管理器中查看进程的 CPU 和内存占用，那么如何通过 .NET 编写代码的方式来获取到 CPU 和内存占用呢？&lt;/p&gt;

&lt;p&gt;.NET 中提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PerformanceCounter&lt;/code&gt; 类型，可以用来监视系统中大量的性能问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;获取全局-cpu-和内存占用&quot;&gt;获取全局 CPU 和内存占用&lt;/h2&gt;

&lt;p&gt;要获取到全系统中的 CPU 占用率，获取全系统中内存占用，需要首先分别创建这两者的性能计数器：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 创建对 CPU 占用百分比的性能计数器。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cpuCounter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformanceCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Processor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;% Processor Time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_Total&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 创建内存占用字节数的性能计数器&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ramCounter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformanceCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Available MBytes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;NextValue()&lt;/code&gt; 可分别获取到两者的值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cpu&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CpuCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NextValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ram&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RamCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NextValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你需要注意的是，我们在创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;PerformanceCounter&lt;/code&gt; 时，构造函数中传入的参数是固定的，或者说必须跟当前系统中安装的计数器的计数器类别的名称（&lt;code class=&quot;highlighter-rouge&quot;&gt;categoryName&lt;/code&gt;，第一个参数）和计数器的名称（&lt;code class=&quot;highlighter-rouge&quot;&gt;counterName&lt;/code&gt;，第二个参数）对应。另外，如果某个类别包含单个实例，那么需要传入实例名称（&lt;code class=&quot;highlighter-rouge&quot;&gt;instanceName&lt;/code&gt;，第三个参数）。&lt;/p&gt;

&lt;h2 id=&quot;获取当前进程的-cpu-和内存占用&quot;&gt;获取当前进程的 CPU 和内存占用&lt;/h2&gt;

&lt;p&gt;在了解的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PerformanceCounter&lt;/code&gt; 各个参数代表的含义之后，我们还可以获取到单个进程的性能计数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cpuCounter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformanceCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Process&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;% Processor Time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ramCounter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformanceCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Process&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Working Set&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;NextValue()&lt;/code&gt; 来获取到此性能计数器实例的值。&lt;/p&gt;

&lt;p&gt;这里，我们在计算单个进程的内存占用时，使用的是工作集大小，这个值会比较接近我们平时使用任务管理器看到的物理内存占用的大小，但是我们还有其他可以查询的类别：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Private Bytes&lt;/code&gt;
  包含进程向系统中申请的私有内存大小，不包含跨进程中共享的部分内存。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Working Set&lt;/code&gt;
  进程占用的物理内存的大小。由于包含共享内存部分和其他资源，所以其实并不准；但这个值就是在任务管理器中看到的值。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Virtual Bytes&lt;/code&gt;
  进程在地址空间中已经使用到的所有的地址空间总大小。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.performancecounter&quot;&gt;PerformanceCounter Class (System.Diagnostics) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/278088/6233938&quot;&gt;How to get the CPU Usage in C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/14802787/6233938&quot;&gt;.net - Get CPU Usage for Process by Process ID - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/4680030/6233938&quot;&gt;c# - What is the correct Performance Counter to get CPU and Memory Usage of a Process? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/1984186/6233938&quot;&gt;debugging - What is private bytes, virtual bytes, working set? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 27 Jun 2019 23:48:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-get-process-cpu-memory-usage.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-get-process-cpu-memory-usage.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>从 git 的历史记录中彻底删除文件或文件夹</title>
        <description>&lt;p&gt;如果你对外开源的代码中出现了敏感信息（例如你将私钥上传到了仓库中），你可能需要考虑将这个文件从 git 的历史记录中完全删除掉。&lt;/p&gt;

&lt;p&gt;本文介绍如何从 git 的历史记录中彻底删除文件或文件夹。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;第一步修改本地历史记录&quot;&gt;第一步：修改本地历史记录&lt;/h2&gt;

&lt;p&gt;彻底删除文件：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter-branch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--index-filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'git rm --cached --ignore-unmatch walterlv.xml'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--prune-empty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--tag-name-filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.xml&lt;/code&gt; 是本来不应该上传的私钥文件，于是使用此命令彻底删除。后面的命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;--tag-name-filter&lt;/code&gt; 指所有相关的标签都需要更新。&lt;/p&gt;

&lt;p&gt;彻底删除文件夹：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter-branch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--index-filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'git rm --cached -r --ignore-unmatch WalterlvDemoFolder'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--prune-empty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--tag-name-filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;删除文件夹时需要额外带一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;-r&lt;/code&gt; 选项，并指定文件夹名称，这里的例子是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoFolder&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;第二步强制推送到远端仓库&quot;&gt;第二步：强制推送到远端仓库&lt;/h2&gt;

&lt;p&gt;刚刚我们的操作仅仅发生在本地仓库，敏感信息需要删除的仓库通常都在远端，于是我们一定要将修改推送到远端仓库。&lt;/p&gt;

&lt;p&gt;需要推送的目标分支包括我们所有长期维护的分支，这通常就包括了 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支和所有的标签。&lt;/p&gt;

&lt;p&gt;于是使用推送命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;git.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;master:master&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 23 Jun 2019 08:43:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/remove-files-or-folders-from-git-history.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/remove-files-or-folders-from-git-history.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET 中让 Task 支持带超时的异步等待</title>
        <description>&lt;p&gt;Task 自带有很多等待任务完成的方法，有的是实例方法，有的是静态方法。有的阻塞，有的不阻塞。不过带超时的方法只有一个，但它是阻塞的。&lt;/p&gt;

&lt;p&gt;本文将介绍一个非阻塞的带超时的等待方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;task-已有的等待方法&quot;&gt;Task 已有的等待方法&lt;/h2&gt;

&lt;p&gt;Task 实例已经有的等待方法有这些：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-08-21-19-48.png&quot; alt=&quot;Task 实例的等待方法&quot; /&gt;&lt;br /&gt;
▲ Task 实例的等待方法&lt;/p&gt;

&lt;p&gt;一个支持取消，一个支持超时，再剩下的就是这两个的排列组合了。&lt;/p&gt;

&lt;p&gt;但是 Task 实例的等待方法都有一个弊端，就是 &lt;strong&gt;阻塞&lt;/strong&gt;。如果你真的试图去等待这个 Task，势必会占用一个宝贵的线程资源。所以通常不建议这么做。&lt;/p&gt;

&lt;p&gt;另外，Task 还提供了静态的等待方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-08-21-30-15.png&quot; alt=&quot;Task 静态的等待方法&quot; /&gt;&lt;br /&gt;
▲ Task 静态的等待方法&lt;/p&gt;

&lt;p&gt;Task.Wait 提供的功能几乎与 Task 实例的 Wait 方法是一样的，只是可以等待多个 Task 的实例。而 Task.When 则是真正的异步等待，不阻塞线程的，可以节省一个线程资源。&lt;/p&gt;

&lt;p&gt;可是，依然只有 Task.Wait 这种阻塞的方法才有超时，Task.When 系列是没有的。&lt;/p&gt;

&lt;h2 id=&quot;我们补充一个带超时的异步等待方法&quot;&gt;我们补充一个带超时的异步等待方法&lt;/h2&gt;

&lt;p&gt;Task 有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delay&lt;/code&gt; 静态方法，我们是否可以利用这个方法来间接实现异步非阻塞的等待呢？&lt;/p&gt;

&lt;p&gt;答案是可以的，我们有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.WhenAny&lt;/code&gt; 可以在多个任务的任何一个完成时结束。我们的思路是要么任务先完成，要么超时先完成。&lt;/p&gt;

&lt;p&gt;于是我们可以先建一个新的 Task，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Delay(timeout)&lt;/code&gt;，再比较这两个 Task 的执行先后：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WaitAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeoutException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The operation has timed out.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;考虑延时任务可以取消，于是我们可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationTokenSource&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;将这个方法封装成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的扩展方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TaskWaitingExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WaitAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeoutCancellationTokenSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CancellationTokenSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delayTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeoutCancellationTokenSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delayTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;timeoutCancellationTokenSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeoutException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The operation has timed out.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们就可以在任意的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 实例上调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.WaitAsync&lt;/code&gt; 来获取带超时的等待了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;[c# - Asynchronously wait for Task&lt;T&gt; to complete with timeout - Stack Overflow](https://stackoverflow.com/q/4238345/6233938)&lt;/T&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 21 Jun 2019 01:17:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/task-wait-async-with-timeout.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/task-wait-async-with-timeout.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 使用 ILMerge 合并多个程序集，避免引入额外的依赖</title>
        <description>&lt;p&gt;我们有多种工具可以将程序集合并成为一个。打包成一个程序集可以避免分发程序的时候带上一堆依赖而出问题。&lt;/p&gt;

&lt;p&gt;ILMerge 可以用来将多个程序集合并成一个程序集。本文介绍使用 ILMerge 工具和其 NuGet 工具包来合并程序集和其依赖。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;以-nuget-包的形式使用-ilmerge&quot;&gt;以 NuGet 包的形式使用 ILMerge&lt;/h2&gt;

&lt;p&gt;ILMerge 提供了可供你项目使用的 NuGet 包。如果你在团队项目当中安装了 ILMerge 的 NuGet 包，那么无论团队其他人是否安装了 ILMerge 的工具，都可以使用 ILMerge 工具。这可以避免要求团队所有成员安装工具或者将工具内置到项目的源代码管理中。&lt;/p&gt;

&lt;p&gt;要以 NuGet 包的形式来使用 ILMerge，需要首先安装 ILMerge 的 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[NuGet Gallery&lt;/td&gt;
          &lt;td&gt;ilmerge](https://www.nuget.org/packages/ilmerge)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;或者直接在你的项目的 csproj 文件中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ILMerge&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.0.29&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我现在有一个项目 Walterlv.Demo.AssemblyLoading，这是一个控制台程序。这个程序引用了一个 NuGet 包 Ben.Demystifier。为此带来了三个额外的依赖。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Walterlv.Demo.AssemblyLoading.exe
- Ben.Demystifier.dll
- System.Collections.Immutable.dll
- System.Reflection.Metadata.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而我们可以使用 ILMerge 将这些依赖和我们生成的主程序合并成一个程序集，这样分发程序的时候只需要一个程序集即可。&lt;/p&gt;

&lt;p&gt;那么，我们现在需要编辑我们的项目文件：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        
        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Ben.Demystifier&quot; Version=&quot;0.1.4&quot; /&amp;gt;
            &amp;lt;PackageReference Include=&quot;ILMerge&quot; Version=&quot;3.0.29&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;

++      &amp;lt;Target Name=&quot;ILMerge&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;Exec Command=&quot;&amp;amp;quot;$(ILMergeConsolePath)&amp;amp;quot; /ndebug /target:exe /out:$(OutputPath)$(AssemblyName).exe /log $(OutputPath)$(AssemblyName).exe /log $(OutputPath)Ben.Demystifier.dll /log $(OutputPath)System.Collections.Immutable.dll /log $(OutputPath)System.Reflection.Metadata.dll /targetplatform:v4&quot; /&amp;gt;
++      &amp;lt;/Target&amp;gt;
&lt;/span&gt;    
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们只增加了三行，添加了一个名称为 ILMerge 的 Target。（注意到项目文件中我有额外引用一个其他的 NuGet 包 Ben.Demystifier，这是为了演示将依赖进行合并而添加的 NuGet 包，具体是什么都没有关系，我们只是在演示依赖的合并。）在这个 Target 里面，我们使用 Exec 的 Task 来执行 ILMerge 命令。具体这个命令代表的含义我们在下一节介绍 ILMerge 工具的时候会详细介绍。如果你希望在你的项目当中进行尝试，可以把所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;/log&lt;/code&gt; 参数之后的那些程序集名称改为你自己的名称。&lt;/p&gt;

&lt;p&gt;那么在编译的时候使用命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild /t:ILMerge&lt;/code&gt; 就可以完成程序集的合并了。&lt;/p&gt;

&lt;p&gt;注意，你普通编译的话是不会进行 IL 合并的。&lt;/p&gt;

&lt;p&gt;如果你希望常规编译也可以进行 IL 合并，或者说希望在 Visual Studio 里面点击生成按钮的时候也能完成 IL 合并的话，那么你还需要增加一个跳板的编译目标 Target。&lt;/p&gt;

&lt;p&gt;我将这个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_ProjectRemoveDependencyFiles&lt;/code&gt; 的 Target 增加到了下面。它的目的是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt; 这个编译目标完成之后（AfterTargets）执行，然后执行前需要先执行（DependsOnTargets）ILMerge 这个 Target。在这个编译目标执行的时候还会将原本的三个依赖删除掉，这样在生成的目录下我们将只会看到我们最终期望的程序集 Walterlv.Demo.AssemblyLoading.exe 而没有其他依赖程序集。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        
        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Ben.Demystifier&quot; Version=&quot;0.1.4&quot; /&amp;gt;
            &amp;lt;PackageReference Include=&quot;ILMerge&quot; Version=&quot;3.0.29&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;

        &amp;lt;Target Name=&quot;ILMerge&quot;&amp;gt;
            &amp;lt;Exec Command=&quot;&amp;amp;quot;$(ILMergeConsolePath)&amp;amp;quot; /ndebug /target:exe /out:$(OutputPath)$(AssemblyName).exe /log $(OutputPath)$(AssemblyName).exe /log $(OutputPath)Ben.Demystifier.dll /log $(OutputPath)System.Collections.Immutable.dll /log $(OutputPath)System.Reflection.Metadata.dll /targetplatform:v4&quot; /&amp;gt;
        &amp;lt;/Target&amp;gt;

++      &amp;lt;Target Name=&quot;_ProjectRemoveDependencyFiles&quot; AfterTargets=&quot;AfterBuild&quot; DependsOnTargets=&quot;ILMerge&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;ItemGroup&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)Ben.Demystifier.dll&quot; /&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)System.Collections.Immutable.dll&quot; /&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)System.Reflection.Metadata.dll&quot; /&amp;gt;
++          &amp;lt;/ItemGroup&amp;gt;
++          &amp;lt;Delete Files=&quot;@(_ProjectDependencyFile)&quot; /&amp;gt;
++      &amp;lt;/Target&amp;gt;
&lt;/span&gt;    
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终生成的输出目录下只有我们最终期望生成的程序集：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-13-08-08-00.png&quot; alt=&quot;最终生成的程序集&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;以命令行工具的形式使用-ilmerge&quot;&gt;以命令行工具的形式使用 ILMerge&lt;/h2&gt;

&lt;p&gt;你可以在这里下载到 ILMerge：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=17630&quot;&gt;Download ILMerge from Official Microsoft Download Center&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实际上 ILMerge 已经开源，你可以在 GitHub 上找到它：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge&quot;&gt;dotnet/ILMerge: ILMerge is a static linker for .NET Assemblies.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;装完之后，如果将 ILMerge 的可执行目录加入到环境变量，那么你将可以在任意的目录下在命令行中直接使用 ILMerge 命令了。加入环境变量的方法我就不用说了，可以在网上搜索到非常多的资料。&lt;/p&gt;

&lt;p&gt;ILMerge 装完的默认目录在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft\ILMerge&lt;/code&gt;，所以如果你保持默认路径安装，那么几乎可以直接把这个路径加入到环境变量中。&lt;/p&gt;

&lt;p&gt;那么 ILMerge 的命令行如何使用呢？它的参数列表是怎样的呢？&lt;/p&gt;

&lt;p&gt;我们来写一个简单的例子：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ilmerge&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/ndebug&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/target:exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ben.Demystifier.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Collections.Immutable.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Reflection.Metadata.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/targetplatform:v4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/ndebug&lt;/code&gt; 表示以非调试版本编译，如果去掉，将会生成 pdb 文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/target&lt;/code&gt; 合并之后的程序集类型，如果是控制台程序，则为 exe&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/out&lt;/code&gt; 输出文件的名称（或路径）（此路径可以和需要合并的程序集名称相同，这样在合并完之后会覆盖同名称的那个程序集）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/log&lt;/code&gt; 所有需要合并的程序集名称（或路径）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/targetplatform&lt;/code&gt; 目标平台，如果是 .NET Framework 4.0 - .NET Framework 4.8 之间，则都是 v4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在合并完成之后，我们反编译可以发现程序集中已经包含了依赖程序集中的全部类型了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-13-08-40-58.png&quot; alt=&quot;合并后的程序集&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;以封装的-nuget-包来使用-ilrepack&quot;&gt;以封装的 NuGet 包来使用 ILRepack&lt;/h2&gt;

&lt;p&gt;安装 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/ILRepack.Lib.MSBuild.Task/&quot;&gt;NuGet Gallery - ILRepack.Lib.MSBuild.Task&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;之后，你就能直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ILRepack&lt;/code&gt; 这个编译任务了，而不是在 MSBuild 中使用 Exec 来间接执行 ILRepack 的任务。&lt;/p&gt;

&lt;p&gt;关于此 NuGet 包的使用，GitHub 中有很棒的例子，可以查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/peters/ILRepack.MSBuild.Task&quot;&gt;peters/ILRepack.MSBuild.Task: MSBuild task for ILRepack which is an open-source alternative to ILMerge.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;需要注意&quot;&gt;需要注意&lt;/h2&gt;

&lt;p&gt;如果使用新的基于 Sdk 的项目文件，那么默认生成的 PDB 是 Portable PDB，但是 ILMerge 暂时不支持 Portable PDB，会在编译时提示错误：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;An exception occurred during merging:
ILMerge.Merge:  There were errors reported in dotnetCampus.EasiPlugin.Sample's metadata.
        数组维度超过了支持的范围。
   在 ILMerging.ILMerge.Merge()
   在 ILMerging.ILMerge.Main(String[] args)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者英文提示：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;An exception occurred during merging:
ILMerge.Merge:        There were errors reported in ReferencedProject's metadata.
      Array dimensions exceeded supported range.
   at ILMerging.ILMerge.Merge()
   at ILMerging.ILMerge.Main(String[] args)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;目前，GitHub 上有 issue 在追踪此问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge/issues/11&quot;&gt;Support for portable PDBs · Issue #11 · dotnet/ILMerge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/cncc/p/7777374.html&quot;&gt;[C#]使用ILMerge将源DLL合并到目标EXE(.NET4.6.2) - cnc - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge&quot;&gt;dotnet/ILMerge: ILMerge is a static linker for .NET Assemblies.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/ilmerge&quot;&gt;NuGet Gallery - ilmerge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jbevain/cecil&quot;&gt;jbevain/cecil: Cecil is a library to inspect, modify and create .NET programs and libraries.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack&quot;&gt;gluck/il-repack: Open-source alternative to ILMerge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge/issues/11&quot;&gt;Support for portable PDBs · Issue #11 · dotnet/ILMerge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.meziantou.net/merging-assemblies-using-ilrepack.htm&quot;&gt;Merging assemblies using ILRepack - Meziantou’s blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/peters/ILRepack.MSBuild.Task&quot;&gt;peters/ILRepack.MSBuild.Task: MSBuild task for ILRepack which is an open-source alternative to ILMerge.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 17 Jun 2019 13:34:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/merge-assemblies-using-ilmerge.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/merge-assemblies-using-ilmerge.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 使用 ILRepack 合并多个程序集（替代 ILMerge），避免引入额外的依赖</title>
        <description>&lt;p&gt;我们有多种工具可以将程序集合并成为一个。比如 ILMerge、Mono.Merge。前者不可定制、运行缓慢、消耗资源（不过好消息是现在开源了）；后者已被弃用、不受支持且基于旧版本的 Mono.Cecil。&lt;/p&gt;

&lt;p&gt;而本文介绍用来替代它们的 ILRepack，使用 ILRepack 来合并程序集。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;以-nuget-包的形式使用-ilrepack&quot;&gt;以 NuGet 包的形式使用 ILRepack&lt;/h2&gt;

&lt;p&gt;ILRepack 提供了可供你项目使用的 NuGet 包。如果你在团队项目当中安装了 ILRepack 的 NuGet 包，那么无论团队其他人是否安装了 ILRepack 的工具，都可以使用 ILRepack 工具。这可以避免要求团队所有成员安装工具或者将工具内置到项目的源代码管理中。&lt;/p&gt;

&lt;p&gt;要以 NuGet 包的形式来使用 ILRepack，需要首先安装 ILRepack 的 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[NuGet Gallery&lt;/td&gt;
          &lt;td&gt;ILRepack](https://www.nuget.org/packages/ILRepack/)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;或者直接在你的项目的 csproj 文件中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ILRepack&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.17&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我现在有一个项目 Walterlv.Demo.AssemblyLoading，这是一个控制台程序。这个程序引用了一个 NuGet 包 Ben.Demystifier。为此带来了三个额外的依赖。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Walterlv.Demo.AssemblyLoading.exe
- Ben.Demystifier.dll
- System.Collections.Immutable.dll
- System.Reflection.Metadata.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而我们可以使用 ILRepack 将这些依赖和我们生成的主程序合并成一个程序集，这样分发程序的时候只需要一个程序集即可。&lt;/p&gt;

&lt;p&gt;那么，我们现在需要编辑我们的项目文件：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        
        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Ben.Demystifier&quot; Version=&quot;0.1.4&quot; /&amp;gt;
            &amp;lt;PackageReference Include=&quot;ILRepack&quot; Version=&quot;2.0.17&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;

++      &amp;lt;Target Name=&quot;ILRepack&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;Exec Command=&quot;&amp;amp;quot;$(ILRepack)&amp;amp;quot; /out:$(OutputPath)$(AssemblyName).exe $(OutputPath)$(AssemblyName).exe $(OutputPath)Ben.Demystifier.dll $(OutputPath)System.Collections.Immutable.dll $(OutputPath)System.Reflection.Metadata.dll&quot; /&amp;gt;
++      &amp;lt;/Target&amp;gt;
&lt;/span&gt;    
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们只增加了三行，添加了一个名称为 ILRepack 的 Target。（注意到项目文件中我有额外引用一个其他的 NuGet 包 Ben.Demystifier，这是为了演示将依赖进行合并而添加的 NuGet 包，具体是什么都没有关系，我们只是在演示依赖的合并。）在这个 Target 里面，我们使用 Exec 的 Task 来执行 ILRepack 命令。具体这个命令代表的含义我们在下一节介绍 ILRepack 工具的时候会详细介绍。如果你希望在你的项目当中进行尝试，可以把后面那些代表程序集的名称改为你自己项目中依赖程序集的名称。&lt;/p&gt;

&lt;p&gt;现在在编译的时候使用命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild /t:ILRepack&lt;/code&gt; 就可以完成程序集的合并了。&lt;/p&gt;

&lt;p&gt;注意，你普通编译的话是不会进行 IL 合并的。&lt;/p&gt;

&lt;p&gt;如果你希望常规编译也可以进行 IL 合并，或者说希望在 Visual Studio 里面点击生成按钮的时候也能完成 IL 合并的话，那么你还需要增加一个跳板的编译目标 Target。&lt;/p&gt;

&lt;p&gt;我将这个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_ProjectRemoveDependencyFiles&lt;/code&gt; 的 Target 增加到了下面。它的目的是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt; 这个编译目标完成之后（AfterTargets）执行，然后执行前需要先执行（DependsOnTargets）ILRepack 这个 Target。在这个编译目标执行的时候还会将原本的三个依赖删除掉，这样在生成的目录下我们将只会看到我们最终期望的程序集 Walterlv.Demo.AssemblyLoading.exe 而没有其他依赖程序集。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;

        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Ben.Demystifier&quot; Version=&quot;0.1.4&quot; /&amp;gt;
            &amp;lt;PackageReference Include=&quot;ILRepack&quot; Version=&quot;2.0.17&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;

        &amp;lt;Target Name=&quot;ILRepack&quot;&amp;gt;
            &amp;lt;Exec Command=&quot;&amp;amp;quot;$(ILRepack)&amp;amp;quot; /out:$(OutputPath)$(AssemblyName).exe $(OutputPath)$(AssemblyName).exe $(OutputPath)Ben.Demystifier.dll $(OutputPath)System.Collections.Immutable.dll $(OutputPath)System.Reflection.Metadata.dll&quot; /&amp;gt;
        &amp;lt;/Target&amp;gt;

++      &amp;lt;Target Name=&quot;_ProjectRemoveDependencyFiles&quot; AfterTargets=&quot;AfterBuild&quot; DependsOnTargets=&quot;ILRepack&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++          &amp;lt;ItemGroup&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)Ben.Demystifier.dll&quot; /&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)System.Collections.Immutable.dll&quot; /&amp;gt;
++              &amp;lt;_ProjectDependencyFile Include=&quot;$(OutputPath)System.Reflection.Metadata.dll&quot; /&amp;gt;
++              &amp;lt;/ItemGroup&amp;gt;
++          &amp;lt;Delete Files=&quot;@(_ProjectDependencyFile)&quot; /&amp;gt;
++      &amp;lt;/Target&amp;gt;
&lt;/span&gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终生成的输出目录下只有我们最终期望生成的程序集：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-13-09-30-59.png&quot; alt=&quot;最终生成的程序集&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ilrepack-的命令行使用&quot;&gt;ILRepack 的命令行使用&lt;/h2&gt;

&lt;p&gt;相比于 ILMerge，ILRepack 的命令行在尽量贴近 ILMerge 的情况下做得更加简化了。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ilrepack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ben.Demystifier.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Collections.Immutable.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Reflection.Metadata.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;/out&lt;/code&gt; 表示最终的输出程序集的名称或路径，后面没有前缀的参数都是需要合并的程序集的名称或路径。这些需要合并的参数中，第一个参数是主程序集，而后续其他的都是待合并的程序集。区别主程序集和其他程序集的原因是输出的程序集需要有名称、版本号等等信息，而这些信息将使用主程序集中的信息。&lt;/p&gt;

&lt;p&gt;如果希望使用 ILRepack 的其他命令，可以考虑使用帮助命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ilrepack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/help&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者直接访问 ILRepack 的 GitHub 仓库来查看用法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack&quot;&gt;gluck/il-repack: Open-source alternative to ILMerge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如果解决合并错误&quot;&gt;如果解决合并错误？&lt;/h2&gt;

&lt;h3 id=&quot;缺少依赖&quot;&gt;缺少依赖&lt;/h3&gt;

&lt;p&gt;如果你在使用 ILRepack 合并程序集的过程中出现了缺少依赖的错误，例如下面这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'xxxxxxxxx'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-13-13-51-42.png&quot; alt=&quot;缺少依赖错误提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么你需要做以下两种事情中的任何一种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将所有依赖合并；&lt;/li&gt;
  &lt;li&gt;将依赖加入搜索目录。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;将所有依赖合并指的是将缺少的依赖也一起作为命令行参数传入要合并的程序集中。&lt;/p&gt;

&lt;p&gt;而另一种是增加一个参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;/lib&lt;/code&gt;，即添加一个被搜索的依赖程序集的目录。将这个目录指定后，则可以正确解析依赖完成合并。而且这些依赖将成为合并后的程序集的依赖，不会合并到程序集中。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ilrepack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/lib:D:\Dependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.AssemblyLoading.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ben.Demystifier.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Collections.Immutable.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Reflection.Metadata.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;没有生成-pdb-文件&quot;&gt;没有生成 PDB 文件&lt;/h3&gt;

&lt;p&gt;如果使用新的基于 Sdk 的项目文件，那么默认生成的 PDB 是 Portable PDB，但是 ILRepack 暂时不支持 Portable PDB，其在内部捕获了异常以至于可以完成合并但不会生成 PDB 文件。&lt;/p&gt;

&lt;p&gt;目前此问题在 ILRepack 中还处于打开状态，且持续两年都没关闭了。同时很早就有支持 Portable PDB 的拉取请求，但至今未合并。&lt;/p&gt;

&lt;p&gt;以下是 GitHub 社区中的讨论：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack/issues/182&quot;&gt;Mono.Cecil 0.10 support · Issue #182 · gluck/il-repack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack/pull/236&quot;&gt;Migrate to vanilla 0.10 cecil by Alexx999 · Pull Request #236 · gluck/il-repack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack/issues/230&quot;&gt;ERROR: Failed to load assembly while merging .NET Core assembly · Issue #230 · gluck/il-repack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/ILMerge/issues/11&quot;&gt;Support for portable PDBs · Issue #11 · dotnet/ILMerge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack&quot;&gt;gluck/il-repack: Open-source alternative to ILMerge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gluck/il-repack/issues/217&quot;&gt;Is it expected that pdb files are not merged? · Issue #217 · gluck/il-repack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 17 Jun 2019 13:30:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/merge-assemblies-using-ilrepack.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/merge-assemblies-using-ilrepack.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 10 解决无法完整下载安装语言包（日语输入法无法下载使用）</title>
        <description>&lt;p&gt;最近我想在我的 Windows 10 上安装一个新的语言包，在 “设置” -&amp;gt; “时间和语言” -&amp;gt; “语言” 中，添加了新的语言之后，语言进入了下载状态。但是没过一小会儿，下载进度条就结束了，提示语言已经下载安装完成。但实际上只能作为显示使用，（日语）输入法却不能使用。&lt;/p&gt;

&lt;p&gt;我找了很多的资料试图解决这个问题，但发现竟然没有任何一种现有方法可以解决我的问题（这可能是日语输入法特有的问题吧）。最终解决后，我将网上搜集到的方法以及我实际解决所使用的方法都收录进来，方便大家后续解决问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题描述&quot;&gt;问题描述&lt;/h2&gt;

&lt;p&gt;网上找到了一段跟我几乎一样的描述，&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/win10%E6%97%A0%E6%B3%95%E5%AE%8C%E6%95%B4%E4%B8%8B/22663fe8-5fd8-402d-bcc8-b4ce0c2e38b0&quot;&gt;可以前往这里查看&lt;/a&gt;。我发现他描述得非常准确，所以就直接引用了他的原话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;添加语言的时候能下载显示语言，点进选项后发现输入语言没有自动下载和安装，手动点下载，进度条在卡在前半不动，几秒后自动跳掉。&lt;/p&gt;

  &lt;p&gt;造成的影响是：1.日文输入法能出现，但无法切换到假名状态，只能输入英文；……&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我能够添加完成日语，并且它也能作为我的显示语言正常显示。但是进入语言之后，发现里面的三个可供下载的扩展选项都没有下载。而如果手动点击下载，无论如何也没有反应。由于输入法就是这里的第一个扩展选项，所以虽然可以切换到日语的微软输入法，但是只能输入英文字母，而无法输入任何日语文字（にほんご）。&lt;/p&gt;

&lt;p&gt;如下图，无论怎么点击都不会下载。重启无效。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-14-22-05-03.png&quot; alt=&quot;怎么点都没反应&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;网上的解决方案有很多种，我这里整理最有可能解决问题的两种。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;删除下载缓存（通用解决方案）&lt;/li&gt;
  &lt;li&gt;暂时关闭 UAC（本次我是此方法成功的）&lt;/li&gt;
  &lt;li&gt;其他方法（请点击本文最后的参考链接，包含我的各种参考资料）&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;删除下载缓存&quot;&gt;删除下载缓存&lt;/h3&gt;

&lt;p&gt;前往文件夹：&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\SoftwareDistribution\Download&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这里面的内容都是 Windows 的各种下载的缓存。如果是因为下载的文件损坏，那么删除此文件夹中的全部内容通常可以解决问题。&lt;/p&gt;

&lt;p&gt;你不用担心删除此文件夹会出现什么问题，因为重新下载那些缓存所付出的代价往往比修复的问题本身更小。&lt;/p&gt;

&lt;p&gt;在时机尝试中，我删除了此文件夹后，重新启动计算机。我发现再点击语言下载之后不会是没有反应了，而是出现了一小会儿的进度条；再随后才继续恢复成没有下载的状态。再之后，也是怎么点击下载也没有反应了。&lt;/p&gt;

&lt;p&gt;于是几乎可以认定语言包的下载缓存确认是在这个路径中的，但是导致无法下载安装的本质原因却不是这个。&lt;/p&gt;

&lt;h3 id=&quot;暂时关闭-uac&quot;&gt;暂时关闭 UAC&lt;/h3&gt;

&lt;p&gt;后来我尝试了网上的其他各种方案，都没有解决。包括删除重新安装语言包，包括使用 PowerShell 脚本删除语言列表项，包括清理注册表项等等。&lt;/p&gt;

&lt;p&gt;我突然间异想天开认为有可能是 UAC（用户账户控制）的问题，但是无论使用中文还是英文搜索，无论使用谷歌还是必应搜索引擎，无论翻了多少页，都没有找到此问题与 UAC 有关的文章、帖子或解决方案。&lt;/p&gt;

&lt;p&gt;但我还是尝试了。&lt;/p&gt;

&lt;p&gt;我打开了 UAC 设置，临时把滑块从最顶部拖到最底部，以关闭 UAC。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-14-23-09-00.png&quot; alt=&quot;UAC 设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“下载”后，终于有反应可以继续完成下载了。看起来是解决了，但这三个下载按钮只有一个可以继续下载安装。但是我重启计算机之后，三个按钮都可以正常点击下载安装了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-14-22-05-23.png&quot; alt=&quot;已经可以开始下载安装了&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-14-22-05-34.png&quot; alt=&quot;已经可以开始下载安装了&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后，我把 UAC 拖到最顶部还原我的设置。&lt;/p&gt;

&lt;p&gt;关于为什么我会拖到最顶部，你可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/there-are-only-two-settings-for-the-uac-slider&quot;&gt;Windows 的 UAC 设置中的通知等级实际上只有两个档而已&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;进程监控与调试&quot;&gt;进程监控与调试&lt;/h3&gt;

&lt;p&gt;当然，我还尝试过使用 Visual Studio 附加 SystemSettings.exe 进程进行调试，发现在每次点击“下载”没有反应的时候会看到出现了一个“线程已结束”的输出，并没有实际上的意义。&lt;/p&gt;

&lt;p&gt;我也希望通过 Process Monitor 查看下载失败时是否涉及到 IO，结果也没有什么线索。&lt;/p&gt;

&lt;h3 id=&quot;其他方法&quot;&gt;其他方法&lt;/h3&gt;

&lt;p&gt;另外，有小伙伴说可以去另一台可以下载安装的电脑上拷贝 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\IME\IMEJP&lt;/code&gt; 目录过来也可以使用。&lt;/p&gt;

&lt;h2 id=&quot;期望&quot;&gt;期望&lt;/h2&gt;

&lt;p&gt;幸好最终解决了问题，希望可以帮到读者。&lt;/p&gt;

&lt;p&gt;如果你有其他方法解决了问题，或者说你试过了各种方法也没有解决问题，欢迎在本文原文的评论区留言，也许能找到更合适的解决办法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/win10%E6%97%A0%E6%B3%95%E5%AE%8C%E6%95%B4%E4%B8%8B/22663fe8-5fd8-402d-bcc8-b4ce0c2e38b0&quot;&gt;WIN10无法完整下载日语语言包，不能下载基本输入语言，不能下载日语补充字库。。&amp;amp;# - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/windows/c5d81b0f-a223-4fad-bb63-df6e82f91a26&quot;&gt;Windows 10（1903）无法下载英语基本输入法、手写、语音功能 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jihosoft.com/tips/download-install-windows-10-language-pack.html&quot;&gt;(Fixed) How to Download and Install Windows 10 Language Pack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/win10%E7%9A%84%E6%97%A5%E8%AF%AD%E8%BE%93%E5%85%A5/846ee4e4-0f15-4431-9faa-b4e170230c4b&quot;&gt;Win10的日语输入法无法安装功能 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/win10%E8%BE%93%E5%85%A5%E6%B3%95%E5%8F%AF%E9%80%89/89f90932-bab3-49e3-b74a-afe272f17461&quot;&gt;win10输入法可选功能无法安装 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/win10/9ae722f4-0c8e-4864-a4e7-018bf478bc87&quot;&gt;win10 可选功能更新（输入法）失败 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/qq_38232598/article/details/80687009&quot;&gt;解决Windows10专业版无法安装语言包！！！ - Antrn的博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/yinghua12a/article/details/81105287&quot;&gt;Win10 1803日文输入法问题 - Kevin的博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 16 Jun 2019 12:53:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-downloading-and-installing-windows-language-pack-issues.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-downloading-and-installing-windows-language-pack-issues.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET 的程序集加载上下文</title>
        <description>&lt;p&gt;我们编写的 .NET 应用程序会使用到各种各样的依赖库。我们都知道 CLR 会在一些路径下帮助我们程序找到依赖，但如果我们需要手动控制程序集加载路径的话，需要了解程序集加载上下文。&lt;/p&gt;

&lt;p&gt;如果你不了解程序集加载上下文，你可能会发现你加载了程序集却不能使用其中的类型；或者把同一个程序集加载了两次，导致使用到两个明明是一样的类型时却抛出异常提示不是同一个类型的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;程序集加载上下文&quot;&gt;程序集加载上下文&lt;/h2&gt;

&lt;p&gt;当你向应用程序域中加载一个程序集时，可能会加载到以下四种不同的上下文中的一种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;默认加载上下文（the Default Load Context）&lt;/li&gt;
  &lt;li&gt;加载位置加载上下文（the Load-From Context）&lt;/li&gt;
  &lt;li&gt;仅反射上下文（the Reflection-Only Context）&lt;/li&gt;
  &lt;li&gt;无上下文&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你需要了解这些加载上下文，因为跨不同加载上下文加载的程序集是不能访问其中的类型的。&lt;/p&gt;

&lt;h3 id=&quot;默认加载上下文&quot;&gt;默认加载上下文&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;在全局程序集缓存中发现的类型会加载到默认加载上下文中&lt;/li&gt;
  &lt;li&gt;位于应用程序探测路径中的程序集会加载到默认加载上下文中，这包括了 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.appdomainsetup.applicationbase&quot;&gt;ApplicationBase&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.appdomainsetup.privatebinpath&quot;&gt;PrivateBinPath&lt;/a&gt; 目录中发现的程序集&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.load&quot;&gt;Assembly.Load&lt;/a&gt; 方法的大多数重载都将程序集加载到此上下文中&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.appdomainsetup.applicationbase&quot;&gt;ApplicationBase&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.appdomainsetup.privatebinpath&quot;&gt;PrivateBinPath&lt;/a&gt; 这两个属性虽然允许被设置，但它们只对新生成的 AppDomain 生效，直接设置当前 AppDomain 中这两个属性的值并不会产生任何效果。&lt;/p&gt;

&lt;p&gt;虽然我们不能直接设置这两个属性，但可以在应用程序的 App.config 文件这配置 &lt;code class=&quot;highlighter-rouge&quot;&gt;configuration -&amp;gt; runtime -&amp;gt; assemblyBinding -&amp;gt; probing.privatePath&lt;/code&gt; 属性来设置多个应用程序执行时的依赖探测路径。&lt;/p&gt;

&lt;p&gt;将程序集加载到默认加载上下文中时，会自动加载其依赖项。&lt;/p&gt;

&lt;p&gt;使用默认加载上下文时，加载到其他上下文中的依赖项将不可用，并且不能将位于探测路径外部位置的程序集加载到默认加载上下文中。&lt;/p&gt;

&lt;h3 id=&quot;加载位置上下文&quot;&gt;加载位置上下文&lt;/h3&gt;

&lt;p&gt;当使用 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.loadfrom&quot;&gt;Assembly.LoadFrom&lt;/a&gt; 方法加载程序集时，程序集会加载到加载位置上下文中。&lt;/p&gt;

&lt;p&gt;如果程序集包含依赖，也会自动从加载位置上下文中加载依赖。另外，在加载位置上下文中加载的程序集，可以使用到默认加载上下文中的依赖；&lt;strong&gt;注意，反过来却不成立！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;加载位置上下文的使用需要&lt;strong&gt;谨慎&lt;/strong&gt;，因为它会产生一些可能让你感觉到意外的行为。以下意外的行为列表照抄自文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading&quot;&gt;Best Practices for Assembly Loading&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;如果已加载一个具有相同标识的程序集，则即使指定了不同的路径，&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.loadfrom&quot;&gt;LoadFrom&lt;/a&gt; 仍返回已加载的程序集。&lt;/li&gt;
    &lt;li&gt;如果用 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.loadfrom&quot;&gt;LoadFrom&lt;/a&gt; 加载一个程序集，随后默认加载上下文中的一个程序集尝试按显示名称加载同一程序集，则加载尝试将失败。 对程序集进行反序列化时，可能发生这种情况。&lt;/li&gt;
    &lt;li&gt;如果用 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.loadfrom&quot;&gt;LoadFrom&lt;/a&gt; 加载一个程序集，并且探测路径包括一个具有相同标识但位置不同的程序集，则将发生 InvalidCastException、MissingMethodException 或其他意外行为。&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.loadfrom&quot;&gt;LoadFrom&lt;/a&gt; 需要对指定路径的 FileIOPermissionAccess.Read 和 FileIOPermissionAccess.PathDiscovery 或 WebPermission。&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;无上下文&quot;&gt;无上下文&lt;/h3&gt;

&lt;p&gt;使用反射发出生成的瞬态程序集只能选择在没有下文的情况下进行加载。在没有上下文的情况下进行加载是将具有同一标识的多个程序集加载到一个应用程序域中的唯一方式。这将省去探测成本。&lt;/p&gt;

&lt;p&gt;从字节数组加载的程序集都是在没有上下文的情况下加载的，除非程序集的标识（在应用策略后建立）与全局程序集缓存中的程序集标识匹配；在此情况下，将会从全局程序集缓存加载程序集。&lt;/p&gt;

&lt;p&gt;在没有上下文的情况下加载程序集具有以下缺点，以下摘抄自 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading&quot;&gt;Best Practices for Assembly Loading&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;无法将其他程序集绑定到在没有上下文的情况下加载的程序集，除非处理 AppDomain.AssemblyResolve 事件。&lt;/li&gt;
    &lt;li&gt;依赖项无法自动加载。 可以在没有上下文的情况下预加载依赖项、将依赖项预加载到默认加载上下文中或通过处理 AppDomain.AssemblyResolve 事件来加载依赖项。&lt;/li&gt;
    &lt;li&gt;在没有上下文的情况下加载具有同一标识的多个程序集会导致出现类型标识问题，这些问题与将具有同一标识的多个程序集加载到多个上下文中所导致的问题类似。 请参阅避免将一个程序集加载到多个上下文中。&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;带来的问题&quot;&gt;带来的问题&lt;/h2&gt;

&lt;p&gt;.NET 加载程序集的这种机制可能让你的程序陷入一点点坑：你可以让你的程序加载任意路径下的一个程序集（dll/exe），并且可以执行其中的代码，但你不能依赖那些路径中程序集的特定类型或接口等。&lt;/p&gt;

&lt;p&gt;具体一点，比如你定义了一个接口 &lt;code class=&quot;highlighter-rouge&quot;&gt;IPlugin&lt;/code&gt;，任意路径中的程序集可以实现这个接口，你加载这个程序集之后也可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;IPlugin&lt;/code&gt; 接口调用到程序集中的方法，因为这个接口的定义所在的程序集依然在你的探测路径中，而不是在插件程序集中。位于任意路径下的插件程序集可以访问到位于探测路径中所有程序集的所有 API，但反过来探测路径下的程序集不能访问到其他目录下插件程序集的特定类型或接口等。但是，如果这个程序集中有一些特定的类型如 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPlugin&lt;/code&gt;，那么你将不能依赖于这个特定的类型。&lt;/p&gt;

&lt;p&gt;我创建了一个控制台程序，用以说明这样的加载上下文机制将带来问题。相关代码可以在我的 GitHub 仓库中找到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.Demo.AssemblyLoading/Walterlv.Demo.AssemblyLoading&quot;&gt;walterlv.demo/Walterlv.Demo.AssemblyLoading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中 Program.cs 文件如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.AssemblyLoading&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadDependencyAssembliesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThrowAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Demystify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThrowAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadDependencyAssembliesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Dependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Ben.Demystifier.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;System.Collections.Immutable.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;System.Reflection.Metadata.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;项目文件 csproj 文件如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ben.Demystifier&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.4&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ProjectMoveDependencies&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_ProjectToMoveFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)Ben.Demystifier.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_ProjectToMoveFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)System.Collections.Immutable.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;_ProjectToMoveFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)System.Reflection.Metadata.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Move&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ProjectToMoveFile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)Dependencies&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这个程序中，我们引用了一个 NuGet 包 &lt;a href=&quot;https://www.nuget.org/packages/Ben.Demystifier/&quot;&gt;Ben.Demystifier&lt;/a&gt;。这个包具体是什么其实并不重要，我只是希望引入一个依赖而已。但是，在项目文件 csproj 中，我写了一个 Target，将这些依赖全部都移动到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dependencies&lt;/code&gt; 文件夹中。这样，我们就可以获得这样目录结构的输出：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Walterlv.exe
- Dependencies
    - Ben.Demystifier.dll
    - System.Collections.Immutable.dll
    - System.Reflection.Metadata.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果我们不进行其他设置，那么直接运行程序的话，应该是找不到依赖然后崩溃的。但是现在我们有 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadDependencyAssembliesAsync&lt;/code&gt; 方法，里面通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.LoadFile&lt;/code&gt; 加载了这三个程序集。但时机运行时依然会崩溃：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-12-11-40-55.png&quot; alt=&quot;抛出异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;明明已经加载了这三个程序集，为什么使用其内部的类型的时候还会抛出异常呢？明明在 Visual Studio 中检查已加载的模块可以发现这些模块都已经加载完毕，但依然无法使用到里面的类型呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-12-14-18-02.png&quot; alt=&quot;已加载模块&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本文将介绍原因和解决办法。&lt;/p&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;p&gt;实际上 .NET 推荐的唯一解决方法是创建新的应用程序域来解决非探测路径下 dll 的依赖问题，在创建新应用程序域的时候设置此应用程序域的探测路径。&lt;/p&gt;

&lt;p&gt;但是，我们其实有其他的方法依然在原来的应用程序域中解决依赖问题。&lt;/p&gt;

&lt;h3 id=&quot;使用被遗弃的-api不推荐&quot;&gt;使用被遗弃的 API（不推荐）&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain&lt;/code&gt; 有一个已经被遗弃的 API &lt;code class=&quot;highlighter-rouge&quot;&gt;AppendPrivatePath&lt;/code&gt;，可以将一个路径加入到探测路径列表中。这样，我们不需要考虑去任意路径加载程序集的问题了，因为我们可以将任意路径设置成探测路径。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注意，这是一个被遗弃的 API。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendPrivatePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于此 API 为什么会被遗弃，你可以阅读微软的官方博客：&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/why-is-appdomain-appendprivatepath-obsolete/&quot;&gt;Why is AppDomain.AppendPrivatePath Obsolete? - .NET Blog&lt;/a&gt;。因为你随时可以指定应用程序的探测路径，所以它可能让你的程序以各种不确定的方式加载程序集，于是你的程序将变得很不稳定；可能完全崩溃到你无法预知的程度。&lt;/p&gt;

&lt;p&gt;另外，.NET Core 中已经不能使用此 API 了，这非常好！&lt;/p&gt;

&lt;h3 id=&quot;使用-ilrepack--ilmerge-合并依赖&quot;&gt;使用 ILRepack / ILMerge 合并依赖&lt;/h3&gt;

&lt;p&gt;前面我们说过，加载位置上下文中的程序集可以依赖默认加载上下文中的程序集，而反过来却不行。通常默认加载上下文中的程序集是我们的主程序程序集和附属程序集，而加载位置上下文中加载的程序是插件程序集。&lt;/p&gt;

&lt;p&gt;如果插件程序集依赖了一些主程序没有的依赖，那么插件可以考虑将所有的依赖合并入插件单个程序集中，避免依赖其他程序集，导致不得不去非探测路径加载程序集。&lt;/p&gt;

&lt;p&gt;关于使用 ILRepack 合并依赖的内容，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/merge-assemblies-using-ilrepack&quot;&gt;.NET 使用 ILRepack 合并多个程序集（替代 ILMerge），避免引入额外的依赖 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先推荐使用 ILRepack 来进行合并，如果你愿意，也可以使用 ILMerge：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/merge-assemblies-using-ilmerge&quot;&gt;.NET 使用 ILMerge 合并多个程序集，避免引入额外的依赖&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-12-16-05-44.png&quot; alt=&quot;使用 ILMerge 合并依赖&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblog.west-wind.com/posts/2016/dec/12/loading-net-assemblies-out-of-seperate-folders&quot;&gt;Loading .NET Assemblies out of Seperate Folders - Rick Strahl’s Web Log&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading&quot;&gt;Best Practices for Assembly Loading - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/why-is-appdomain-appendprivatepath-obsolete/&quot;&gt;Why is AppDomain.AppendPrivatePath Obsolete? - .NET Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 13 Jun 2019 05:49:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/assembly-loading-context.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/assembly-loading-context.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 程序的编译过程</title>
        <description>&lt;p&gt;基于 Sdk 的项目进行编译的时候，会使用 Sdk 中附带的 props 文件和 targets 文件对项目进行编译。Microsoft.NET.Sdk.WindowsDesktop 的 Sdk 包含 WPF 项目的编译过程。&lt;/p&gt;

&lt;p&gt;而本文介绍 WPF 项目的编译过程，包含 WPF 额外为编译过程添加的那些扩展编译目标，以及这些扩展的编译目标如何一步步完成 WPF 项目的过程。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;提前准备&quot;&gt;提前准备&lt;/h2&gt;

&lt;p&gt;在阅读本文之前，你可能需要提前了解编译过程到底是怎样的。可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你不明白上面文章中的一些术语（例如 Target / Task），可能不能理解本文后面的内容。&lt;/p&gt;

&lt;p&gt;另外，除了本文所涉及的内容之外，你也可以自己探索编译过程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WPF 的编译代码都在 Microsoft.WinFx.targets 文件中，你可以通过上面这一篇博客找到这个文件。接下来，我们会一一介绍这个文件里面的编译目标（Target），然后统一说明这些 Target 是如何协同工作，将 WPF 程序编译出来的。&lt;/p&gt;

&lt;p&gt;Microsoft.WinFx.targets 的源码可以查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft.WinFx.targets&quot;&gt;wpf/Microsoft.WinFx.targets at master · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;target&quot;&gt;Target&lt;/h2&gt;

&lt;p&gt;WPF 在编译期间会执行以下这些 Target，当然 Target 里面实际用于执行任务的是 Task。&lt;/p&gt;

&lt;p&gt;知道 Target 名称的话，你可以扩展 WPF 的编译过程；而知道 Task 名称的话，可以帮助理解编译过程实际做的事情。&lt;/p&gt;

&lt;p&gt;本文都会列举出来。&lt;/p&gt;

&lt;h3 id=&quot;fileclassification&quot;&gt;FileClassification&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Target 名称：&lt;code class=&quot;highlighter-rouge&quot;&gt;FileClassification&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Task 名称：&lt;code class=&quot;highlighter-rouge&quot;&gt;FileClassifier&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;用于将资源嵌入到程序集。如果资源没有本地化，则嵌入到主程序集；如果有本地化，则嵌入到附属程序集。&lt;/p&gt;

&lt;p&gt;在 WPF 项目中，这个 Target 是一定会执行的；但里面的 Task 则是有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt; 类型的编译项的时候才会执行。&lt;/p&gt;

&lt;h3 id=&quot;generatetemporarytargetassembly&quot;&gt;GenerateTemporaryTargetAssembly&lt;/h3&gt;

&lt;p&gt;Target 名称和 Task 名称相同，都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;只要项目当中包含任何一个生成类型为 Page 的 XAML 文件，则会执行此 Target。&lt;/p&gt;

&lt;p&gt;关于生成临时程序集的原因比较复杂，可以阅读本文后面的 WPF 程序的编译过程部分来了解。&lt;/p&gt;

&lt;h3 id=&quot;markupcompilepass1&quot;&gt;MarkupCompilePass1&lt;/h3&gt;

&lt;p&gt;Target 名称和 Task 名称相同，都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;将非本地化的 XAML 文件编译成二进制格式。&lt;/p&gt;

&lt;h3 id=&quot;markupcompilepass2&quot;&gt;MarkupCompilePass2&lt;/h3&gt;

&lt;p&gt;Target 名称和 Task 名称相同，都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;对 XAML 文件进行第二轮编译，而这一次会引用同一个程序集中的类型。&lt;/p&gt;

&lt;h3 id=&quot;designtimemarkupcompilation&quot;&gt;DesignTimeMarkupCompilation&lt;/h3&gt;

&lt;p&gt;这是一个仅在有设计器执行时才会执行的 Target，当这个编译目标执行时，将会直接调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;实际上，如果在 Visual Studio 中编译项目，则会调用到这个 Target。而判断是否在 Visual Studio 中编译的方法可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/determine-building-in-visual-studio-during-building&quot;&gt;MSBuild 在编写编译任务的时候判断当前是否在 Visual Studio 中编译&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DesignTimeMarkupCompilation&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Only if we are not actually performing a compile i.e. we are in design mode --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CallTarget&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(BuildingProject)' != 'true'&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MarkupCompilePass1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mergelocalizationdirectives&quot;&gt;MergeLocalizationDirectives&lt;/h3&gt;

&lt;p&gt;Target 名称和 Task 名称相同，都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MergeLocalizationDirectives&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;将本地化属性和一个或多个 XAML 二进制格式文件的注释合并到整个程序集的单一文件中。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MergeLocalizationDirectives&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'@(GeneratedLocalizationFiles)' !=''&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Inputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(GeneratedLocalizationFiles)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Outputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)$(AssemblyName).loc&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MergeLocalizationDirectives&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GeneratedLocalizationFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(GeneratedLocalizationFiles)&quot;&lt;/span&gt;
                                &lt;span class=&quot;na&quot;&gt;OutputFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)$(AssemblyName).loc&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--
        Add the merged loc file into _NoneWithTargetPath so that it will be copied to the
        output directory
    --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(IntermediateOutputPath)$(AssemblyName).loc')&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)$(AssemblyName).loc&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;AdditionalMetadata=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CopyToOutputDirectory=PreserveNewest; TargetPath=$(AssemblyName).loc&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_NoneWithTargetPath&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Include&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FileWrites&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Include&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateItem&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mainresourcesgenerationsatelliteresourcegeneration&quot;&gt;MainResourcesGeneration、SatelliteResourceGeneration&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Target 有两个，&lt;code class=&quot;highlighter-rouge&quot;&gt;MainResourcesGeneration&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SatelliteResourceGeneration&lt;/code&gt;，分别负责主资源生成和附属资源生成。&lt;/li&gt;
  &lt;li&gt;Task 名称：&lt;code class=&quot;highlighter-rouge&quot;&gt;ResourcesGenerator&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;将一个或多个资源（二进制格式的 .jpg、.ico、.bmp、XAML 以及其他扩展名类型）嵌入 .resources 文件中。&lt;/p&gt;

&lt;h3 id=&quot;checkuidupdateuidremoveuid&quot;&gt;CheckUid、UpdateUid、RemoveUid&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Target 有三个，&lt;code class=&quot;highlighter-rouge&quot;&gt;CheckUid&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;UpdateUid&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveUid&lt;/code&gt;，分别负责主资源生成和附属资源生成。&lt;/li&gt;
  &lt;li&gt;Task 名称：&lt;code class=&quot;highlighter-rouge&quot;&gt;ResourcesGenerator&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;检查、更新或移除 UID，以便将 XAML 文件中所有的 XAML 元素进行本地化。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CheckUid&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'@(Page)' != '' or '@(ApplicationDefinition)' != ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UidManager&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MarkupFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Page);@(ApplicationDefinition)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Task=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Check&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UpdateUid&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'@(Page)' != '' or '@(ApplicationDefinition)' != ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UidManager&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MarkupFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Page);
                            @(ApplicationDefinition)&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;IntermediateDirectory =&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Task=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Update&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RemoveUid&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'@(Page)' != '' or '@(ApplicationDefinition)' != ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UidManager&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MarkupFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Page);
                            @(ApplicationDefinition)&quot;&lt;/span&gt;

                &lt;span class=&quot;na&quot;&gt;IntermediateDirectory =&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Task=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Remove&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;updatemanifestforbrowserapplication&quot;&gt;UpdateManifestForBrowserApplication&lt;/h3&gt;

&lt;p&gt;当编译基于 XAML 的浏览器项目的时候，会给 manifest 文件中添加一个配置 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;hostInBrowser /&amp;gt;&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;wpf-程序的编译过程&quot;&gt;WPF 程序的编译过程&lt;/h2&gt;

&lt;h3 id=&quot;编译过程图示&quot;&gt;编译过程图示&lt;/h3&gt;

&lt;p&gt;上面列举出来的那些 Target 主要是 WPF 几个关键的 Target，在实际编译时会有更多编译 Target 执行。另外有些也不在常规的编译过程中，而是被专门的编译过程执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-11-32-30.png&quot; alt=&quot;WPF 程序的编译过程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图的阅读方法是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;箭头代表依赖关系，如 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 有一个指向 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesignTimeMarkupCompilation&lt;/code&gt; 的箭头，表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 执行前会确保 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesignTimeMarkupCompilation&lt;/code&gt; 执行完毕；&lt;/li&gt;
  &lt;li&gt;如果一个 Target 有多个依赖，则这些依赖会按顺序执行还没执行的依赖，如 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrepareResources&lt;/code&gt; 指向了多个 Target &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;AfterMarkupCompilePass2&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CleanupTemporaryTargetAssembly&lt;/code&gt;，那么在 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrepareResources&lt;/code&gt; 执行之前，如果还有没有执行的依赖，会按顺序依次执行；&lt;/li&gt;
  &lt;li&gt;WPF 所有的 Target 扩展都是通过依赖来指定的，也就是说必须基于现有的核心编译过程，图中从绿色或黄色的节点向前倒退的所有依赖都会被执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;各种颜色代表的含义：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;蓝色，表示 WPF 扩展的 Target&lt;/li&gt;
  &lt;li&gt;浅蓝色，表示 WPF 扩展的 Target，但是没有执行任何实际的任务，只是提供一个扩展点&lt;/li&gt;
  &lt;li&gt;绿色，表示核心的编译过程，但是被 WPF 编译过程重写了&lt;/li&gt;
  &lt;li&gt;黄色，表示核心的编译过程（即便不是 WPF 程序也会执行的 Target）&lt;/li&gt;
  &lt;li&gt;浅黄色，表示在这张图里面不关心的 Target（不然整个画下来就太多了）&lt;/li&gt;
  &lt;li&gt;紫色，仅在 Visual Studio 编译期间会执行的 WPF 扩展的 Target&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;编译过程描述&quot;&gt;编译过程描述&lt;/h3&gt;

&lt;p&gt;我们都知道 XAML 是可以引用 CLR 类型的；如果 XAML 所引用的 CLR 类型在其他被引用的程序集，那么编译 XAML 的时候就可以直接引用这些程序集，因为他们已经编译好了。&lt;/p&gt;

&lt;p&gt;但是我们也知道，XAML 还能引用同一个程序集中的 CLR 类型，而此时这个程序集还没有编译，XAML 编译过程并不知道可以如何使用这些类型。同时我们也知道 CLR 类型可是使用 XAML 生成的类型，如果 XAML 没有编译，那么 CLR 类型也无法正常完成编译。这是矛盾的，这也是 WPF 扩展的编译过程会比较复杂的原因之一。&lt;/p&gt;

&lt;p&gt;WPF 编译过程有两个编译传递，&lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 的作用是将 XAML 编译成二进制格式。如果 XAML 文件包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Class&lt;/code&gt; 属性，那么就会根据语言生成一份代码文件；对于 C# 语言，会生成“文件名.g.cs”文件。但是 XAML 文件中也有可能包含对同一个程序集中的 CLR 类型的引用，然而这一编译阶段 CLR 类型还没有开始编译，因此无法提供程序集引用。所以如果这个 XAML 文件包含对同一个程序集中 CLR 类型的引用，则这个编译会被推迟到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 中继续。而在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 之间，则插入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 这个编译目标。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 的作用是生成一个临时的程序集，这个临时的程序集中包含了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 推迟到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 中编译时需要的 CLR 类型。这样，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 执行的时候，会获得一个包含原本在统一程序集的 CLR 类型的临时程序集引用，这样就可以继续完成 XAML 格式的编译了。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 编译完成之后，XAML 文件就完全编译完成了。之后，会执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;CleanupTemporaryTargetAssembly&lt;/code&gt; 清除之前临时编译的程序集。&lt;/p&gt;

&lt;p&gt;编译临时程序集时，会生成一个新的项目文件，名字如：&lt;code class=&quot;highlighter-rouge&quot;&gt;$(项目名)_$(随机字符)_wpftmp.csproj&lt;/code&gt;，在与原项目相同的目录下。&lt;/p&gt;

&lt;p&gt;在需要编译一个临时程序集的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 这样的用于编译 C# 代码文件的编译目标会执行两次，第一次是编译这个临时生成的项目，而第二次才是编译原本的项目。&lt;/p&gt;

&lt;p&gt;现在，我们看一段 WPF 程序的编译输出，可以看到看到这个生成临时程序集的过程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-12-38-40.png&quot; alt=&quot;生成临时项目和程序集&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，就是正常的其他的编译过程。&lt;/p&gt;

&lt;h3 id=&quot;关于临时生成程序集&quot;&gt;关于临时生成程序集&lt;/h3&gt;

&lt;p&gt;在 WPF 的编译过程中，我想单独将临时生成程序集的部分进行特别说明。因为如果你不了解这一部分的细节，可能在未来的使用中遇到一些临时生成程序集相关的坑。&lt;/p&gt;

&lt;p&gt;下面这几篇博客就是在讨论其中的一些坑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/issues-of-nuget-package-import-for-wpf-projects&quot;&gt;制作通过 NuGet 分发的源代码包时，如果目标项目是 WPF 则会出现一些问题&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-microsoft.net.sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我需要摘抄生成临时程序集的一部分源码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_CompileTargetNameForLocalType&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(_CompileTargetNameForLocalType)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;_CompileTemporaryAssembly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_CompileTargetNameForLocalType&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_CompileTemporaryAssembly&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BuildOnlySettings;ResolveKeySource;CoreCompile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenerateTemporaryTargetAssembly&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(_RequireMCPass2ForMainAssembly)' == 'true' &quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuildProjectFile is $(MSBuildProjectFile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(MSBuildTargetsVerbose)' == 'true'&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GenerateTemporaryTargetAssembly&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;CurrentProject=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildProjectFullPath)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;MSBuildBinPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildBinPath)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;ReferencePathTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferencePath&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;CompileTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Compile&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;GeneratedCodeFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_GeneratedCodeFiles)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;ReferencePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(ReferencePath)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;IntermediateOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;AssemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AssemblyName)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;CompileTargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_CompileTargetNameForLocalType)&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;GenerateTemporaryTargetAssemblyDebuggingInformation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(GenerateTemporaryTargetAssemblyDebuggingInformation)&quot;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/GenerateTemporaryTargetAssembly&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)$(TargetFileName)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Include&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AssemblyForLocalTypeReference&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateItem&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们需要关注这些点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;生成临时程序集时，会调用一个编译目标（Target），这个编译目标的名称由 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 这个私有属性来决定；&lt;/li&gt;
  &lt;li&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 没有指定时，会设置其默认值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTemporaryAssembly&lt;/code&gt; 这个编译目标；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTemporaryAssembly&lt;/code&gt; 这个编译目标执行时，仅会执行三个依赖的编译目标，&lt;code class=&quot;highlighter-rouge&quot;&gt;BuildOnlySettings&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ResolveKeySource&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt;，至于这些依赖目标所依赖的其他编译目标，则会根据新生成的项目文件动态计算。&lt;/li&gt;
  &lt;li&gt;生成临时程序集和临时程序集的编译过程并不在同一个编译上下文中，这也是为什么只能通过传递名称 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 来执行，而不能直接调用这个编译目标或者设置编译目标的依赖。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;新生成的临时项目文件相比于原来的项目文件，包含了这些修改：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;添加了第一轮 XAML 编译传递（&lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt;）时生成的 .g.cs 文件；&lt;/li&gt;
  &lt;li&gt;将所有引用方式收集到的引用全部换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt;，这样就可以避免临时项目编译期间再执行一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResolveAssemblyReference&lt;/code&gt; 编译目标来收集引用，避免降低太多性能。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于引用换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt; 的内容，可以阅读我的另一篇博客了解更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/resolve-project-references-using-target&quot;&gt;在 Target 中获取项目引用的所有依赖（dll/NuGet/Project）的路径&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt; 的情况下，无论是项目引用还是 NuGet 包引用，都会被换成普通的 dll 引用，因为这个时候目标项目都已经编译完成，包含可以被引用的程序集。&lt;/p&gt;

&lt;p&gt;以下是我在示例程序中抓取到的临时生成的项目文件的内容，与原始项目文件之间的差异：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
            &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
            &amp;lt;GenerateTemporaryTargetAssemblyDebuggingInformation&amp;gt;True&amp;lt;/GenerateTemporaryTargetAssemblyDebuggingInformation&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Walterlv.SourceYard.Demo&quot; Version=&quot;0.1.0-alpha&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;ItemGroup&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationCore.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationFramework.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Core.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Drawing.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Compression.FileSystem.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Numerics.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Runtime.Serialization.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Controls.Ribbon.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xaml.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.Linq.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClient.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClientsideProviders.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationProvider.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationTypes.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsBase.dll&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
++      &amp;lt;ItemGroup&amp;gt;
++          &amp;lt;Compile Include=&quot;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\obj\Debug\net48\Demo.g.cs&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能已经注意到了我在项目中设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssemblyDebuggingInformation&lt;/code&gt; 属性，这个属性可以让 WPF 临时生成的项目文件保留下来，便于进行研究和调试。在前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 的源码部分我们已经贴出了这个属性使用的源码，只是前面我们没有说明其用途。&lt;/p&gt;

&lt;p&gt;注意，虽然新生成的项目文件中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 来表示包引用，但由于只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 指定的编译目标和相关依赖可以被执行，而 NuGet 包中自动 Import 的部分没有加入到依赖项中，所以实际上包中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件都不会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 进来，这可能造成部分 NuGet 包在 WPF 项目中不能正常工作。比如下面这个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/issues-of-nuget-package-import-for-wpf-projects&quot;&gt;制作通过 NuGet 分发的源代码包时，如果目标项目是 WPF 则会出现一些问题&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更典型的，就是 SourceYard 项目，这个 Bug 给 SourceYard 造成了不小的困扰：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.GettingStarted.SourceYard&quot;&gt;walterlv.demo/Walterlv.GettingStarted.SourceYard at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/wpf-msbuild-task-reference?view=vs-2019&quot;&gt;WPF MSBuild Task Reference - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationBuildTasks/BuildTasks/Microsoft/Build/Tasks/Windows/GenerateTemporaryTargetAssembly.cs&quot;&gt;GenerateTemporaryTargetAssembly.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/localization-attributes-and-comments&quot;&gt;Localization Attributes and Comments - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 11 Jun 2019 08:05:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-wpf-assemblies-are-compiled.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-wpf-assemblies-are-compiled.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>制作通过 NuGet 分发的源代码包时，如果目标项目是 WPF 则会出现一些问题（探索篇，含解决方案）</title>
        <description>&lt;p&gt;在使用 NuGet 包来分发源代码时，如果目标项目是 WPF 项目，那么会有一大堆的问题。&lt;/p&gt;

&lt;p&gt;本文将这些问题列举出来并进行分析。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;源代码包&quot;&gt;源代码包&lt;/h2&gt;

&lt;p&gt;源代码包不是 NuGet 官方的概念，而是林德熙和我在 GitHub 上做的一个项目，目的是将你的项目以源代码的形式发布成 NuGet 包。在安装此 NuGet 包后，目标项目将获得这些源代码。&lt;/p&gt;

&lt;p&gt;你可以通过以下博客了解如何制作一个源代码包。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-microsoft.net.sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/sourceyard-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85&quot;&gt;SourceYard 制作源代码包 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这可以避免因为安装 NuGet 包后带来的大量程序集引用，因为程序集数量太多对程序的启动性能有很大的影响：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/c-%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%95%B0%E9%87%8F%E5%AF%B9%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD%E7%9A%84%E5%BD%B1%E5%93%8D&quot;&gt;C# 程序集数量对软件启动性能的影响 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而制作一个 NuGet 的坑很多，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/problems-of-msbuild-and-nuget&quot;&gt;MSBuild/Roslyn 和 NuGet 的 100 个坑&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;基础代码最小的例子&quot;&gt;基础代码：最小的例子&lt;/h2&gt;

&lt;p&gt;为了让 NuGet 源代码包对 WPF 项目问题暴露得更彻底一些，我们需要一个最简单的例子来说明这一问题。我将它放在了我的 Demo 项目中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.GettingStarted.SourceYard&quot;&gt;walterlv.demo/Walterlv.GettingStarted.SourceYard at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但为了让博客理解起来更顺畅，我还是将关键的源代码贴出来。&lt;/p&gt;

&lt;h3 id=&quot;用于打源代码包的项目-walterlvsourceyarddemo&quot;&gt;用于打源代码包的项目 Walterlv.SourceYard.Demo&lt;/h3&gt;

&lt;p&gt;为了尽可能避免其他因素的影响，我们这个源码包只做这些事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;包含一个 targets 文件，用于给目标项目引入源代码；&lt;/li&gt;
  &lt;li&gt;包含一个几乎没有什么代码的 C# 代码文件，用于测试是否正常引入了源代码包；&lt;/li&gt;
  &lt;li&gt;项目的 csproj 文件，用于控制源代码包的编译过程。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体来说，我们的目录结构是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Walterlv.SourceYard.Demo
    - Assets
        - build
            - Package.targets
        - src
            - Foo.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Walterlv.SourceYard.Demo.targets 中的内容如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvIncludeSomeCode&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\src\Foo.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Foo.cs 中的内容如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.SourceYard&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而项目文件（csproj）如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageOutputPath&amp;gt;&lt;/span&gt;..\bin\$(Configuration)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/span&gt;tools&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildOutputTargetFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageRequireLicenseAcceptance&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageRequireLicenseAcceptance&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;0.1.0-alpha&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Company&amp;gt;&lt;/span&gt;dotnet-campus&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Company&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在编译结束后将需要的源码拷贝到 NuGet 包中 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IncludeAllDependencies&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_GetPackageFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\Package.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\src\**&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，编译完成之后，我们可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;..\bin\Debug&lt;/code&gt; 目录下找到我们已经生成好的 NuGet 包，其目录结构如下：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Walterlv.SourceYard.Demo.nupkg
    - build
        - Walterlv.SourceYard.Demo.targets
    - src
        - Foo.cs
    - tools
        - net48
            - Walterlv.SourceYard.Demo.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，那个 Walterlv.SourceYard.Demo.dll 完全没有作用。我们是通过项目中设置了属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;BuildOutputTargetFolder&lt;/code&gt; 让生成的文件跑到这里来的，目的是避免安装此 NuGet 包之后，引用了我们生成的 dll 文件。因为我们要引用的是源代码，而不是 dll。&lt;/p&gt;

&lt;h3 id=&quot;用于验证源代码包的项目-walterlvgettingstartedsourceyardsample&quot;&gt;用于验证源代码包的项目 Walterlv.GettingStarted.SourceYard.Sample&lt;/h3&gt;

&lt;p&gt;现在，我们新建另一个简单的控制台项目用于验证这个 NuGet 包是否正常工作。&lt;/p&gt;

&lt;p&gt;项目文件就是很简单的项目文件，只是我们安装了刚刚生成的 NuGet 包 Walterlv.SourceYard.Demo.nupkg。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net48&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.SourceYard.Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.0-alpha&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而 Program.cs 文件中的内容很简单，只是简单地调用了我们源码包中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo.Run()&lt;/code&gt; 方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.SourceYard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;编译&quot;&gt;编译&lt;/h3&gt;

&lt;p&gt;现在，编译我们的项目，发现完全可以正常编译，就像我在这篇博客中说到的一样：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，事情并不那么简单。接下来全部剩下的都是问题。&lt;/p&gt;

&lt;h2 id=&quot;不可思议的错误&quot;&gt;不可思议的错误&lt;/h2&gt;

&lt;h3 id=&quot;普通控制台项目&quot;&gt;普通控制台项目&lt;/h3&gt;

&lt;p&gt;当我们不进行任何改变，就是以上的代码，对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.GettingStarted.SourceYard.Sample&lt;/code&gt; 项目进行编译（记得提前 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt;），我们可以得到正常的控制台输出。&lt;/p&gt;

&lt;p&gt;注意，我使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild /t:Rebuild&lt;/code&gt; 命令，在编译前进行清理。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/t:Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用于&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成引擎版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;16.1.76&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;g14b0a930a7&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;版权所有&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Corporation&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。保留所有权利。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成启动时间为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/6/10&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;17:32:50&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;erlv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”在节点&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;上&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CheckForNETCoreSdkIsPreview:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files\dotnet\sdk\3.0.100-preview5-011568\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInfer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ence.targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;157&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NETSDK1057:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;你正在使用&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的预览版。请查看&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://aka.ms/dotnet-core-preview&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SourceYard.Sample.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreClean:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在创建目录“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;obj\Debug\net48\&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrepareForBuild:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在创建目录“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bin\Debug\net48\&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateBindingRedirects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResolveAssemblyReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中没有建议的绑定重定向。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateTargetFrameworkMonikerAttribute:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在跳过目标“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateTargetFrameworkMonikerAttribute&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”，因为所有输出文件相对于输入文件而言都是最新的。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CoreCompile:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Studio\2019\Professional\MSBuild\Current\Bin\Roslyn\csc.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/noconfig&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/unsafe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/checked-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/nowarn:1701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1702&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1702&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/nostdlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/platform:AnyCPU&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/errorreport:prompt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warn:4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/define:TRACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ETFRAMEWORK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET48&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/highentropyva&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFra
  mework\v4.8\mscorlib.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v
  4.8\System.Core.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\S
  ystem.Data.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System
  .dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Drawing.d
  ll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Compress
  ion.FileSystem.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\Sy
  stem.Numerics.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\Sys
  tem.Runtime.Serialization.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramew
  ork\v4.8\System.Xml.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4
  .8\System.Xml.Linq.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/debug:portable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/filealign:512&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/optimize-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:obj\Debug\net48\Walterlv.GettingStarte&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d.SourceYard.Sample.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/ruleset:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Team Tools\Static
  Analysis Tools\\Rule Sets\MinimumRecommendedRules.ruleset&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/subsystemversion:6.00&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/target:exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warnaserror-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/utf8outp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ut&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/deterministic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Program.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Users\lvyi\AppData\Local\Temp\.NETFramework,Version=v4.8.AssemblyAttributes.cs&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Users\lvyi\.nuget\packages\walterlv.sourceyard.demo\0.1.0-alpha\build\..\src\Foo.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj\Debug\net48\Walterlv.GettingS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tarted.SourceYard.Sample.AssemblyInfo.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warnaserror&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NU1605&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;对来自后列目录的编译器使用共享编译&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Studio\2019\Professional\MSBuild\Current\Bin\Roslyn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CopyAppConfigFile:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在将文件从“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.exe.withSupportedRuntime.config&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”复制到“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Developments\Open\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\bin\Debug\net48\Walterlv.G&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ettingStarted.SourceYard.Sample.exe.config&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyFilesToOutputDirectory:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在将文件从“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.exe&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”复制到“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.Getti&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ngStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\bin\Debug\net48\Walterlv.GettingStarted.SourceYard.Sam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ple.exe&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Wa&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lterlv.GettingStarted.SourceYard.Sample\bin\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在将文件从“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.pdb&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”复制到“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.Getti&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ngStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\bin\Debug\net48\Walterlv.GettingStarted.SourceYard.Sam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ple.pdb&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已完成生成项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Walterlv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的操作。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;


&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已成功生成。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个警告&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个错误&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已用时间&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;00.59&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，贴一张图片可能更能体现编译通过：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-10-17-45-21.png&quot; alt=&quot;可以编译通过&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上面的输出非常多，但我们提取一下关键的点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有输出的 Target 有这些：&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreClean&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;PrepareForRebuild&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateBindingRedirects&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTargetFrameworkMonikerAttribute&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;_CopyAppConfigFile&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyFilesToOutputDirectory&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;在 CoreCompile 这个编译任务里面，所有需要编译的 C# 代码有这些：&lt;code class=&quot;highlighter-rouge&quot;&gt;Program.cs &quot;C:\Users\lvyi\AppData\Local\Temp\.NETFramework,Version=v4.8.AssemblyAttributes.cs&quot; C:\  Users\lvyi\.nuget\packages\walterlv.sourceyard.demo\0.1.0-alpha\build\..\src\Foo.cs obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.AssemblyInfo.cs&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;可以注意到，编译期间成功将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo.cs&lt;/code&gt; 文件加入了编译。&lt;/p&gt;

&lt;h3 id=&quot;wpf-项目&quot;&gt;WPF 项目&lt;/h3&gt;

&lt;p&gt;现在，我们将我们的项目升级成 WPF 项目。编辑项目文件。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
&lt;/span&gt;
    &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
&lt;/span&gt;    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
        &amp;lt;PackageReference Include=&quot;Walterlv.SourceYard.Demo&quot; Version=&quot;0.1.0-alpha&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;

    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在编译，依然不会出现任何问题，跟控制台程序一模一样。&lt;/p&gt;

&lt;p&gt;但一旦在你的项目中放上一个 XAML 文件，问题立刻变得不一样了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.GettingStarted.SourceYard.Sample.DemoControl&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.GettingStarted.SourceYard.Sample&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/t:Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用于&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成引擎版本&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;16.1.76&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;g14b0a930a7&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;版权所有&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Corporation&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。保留所有权利。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成启动时间为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/6/10&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;17:43:18&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;erlv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”在节点&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;上&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CheckForNETCoreSdkIsPreview:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files\dotnet\sdk\3.0.100-preview5-011568\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInfer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ence.targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;157&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NETSDK1057:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;你正在使用&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的预览版。请查看&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;https://aka.ms/dotnet-core-preview&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SourceYard.Sample.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreClean:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在删除文件“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.csprojAssemblyReference.cache&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在删除文件“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Demo.g.cs&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在删除文件“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample_MarkupCompile.cache&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在删除文件“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e\obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample_MarkupCompile.lref&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateBindingRedirects:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResolveAssemblyReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中没有建议的绑定重定向。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\Walt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;erlv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;正在节点&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;上生成“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ourceYard\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.SourceYard.Sample_vobqk5lg_wpftmp.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CompileTemporaryAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CoreCompile:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Studio\2019\Professional\MSBuild\Current\Bin\Roslyn\csc.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/noconfig&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/unsafe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/checked-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/nowarn:1701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1702&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1702&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/nostdlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/platform:AnyCPU&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/errorreport:prompt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warn:4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/define:TRACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ETFRAMEWORK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET48&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/highentropyva&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFra
  mework\v4.8\mscorlib.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v
  4.8\PresentationCore.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v
  4.8\PresentationFramework.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramew
  ork\v4.8\System.Core.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v
  4.8\System.Data.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\S
  ystem.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Draw
  ing.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Com
  pression.FileSystem.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4
  .8\System.Numerics.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.
  8\System.Runtime.Serialization.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETF
  ramework\v4.8\System.Windows.Controls.Ribbon.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\F
  ramework\.NETFramework\v4.8\System.Xaml.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framew
  ork\.NETFramework\v4.8\System.Xml.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.N
  ETFramework\v4.8\System.Xml.Linq.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NE
  TFramework\v4.8\UIAutomationClient.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.
  NETFramework\v4.8\UIAutomationClientsideProviders.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Micros
  oft\Framework\.NETFramework\v4.8\UIAutomationProvider.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\Mi
  crosoft\Framework\.NETFramework\v4.8\UIAutomationTypes.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/reference:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Reference Assemblies\M
  icrosoft\Framework\.NETFramework\v4.8\WindowsBase.dll&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/debug:portable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/filealign:512&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/optimize-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:obj\Deb&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ug\net48\Walterlv.GettingStarted.SourceYard.Sample.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/ruleset:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Microsoft Visual Studio\2019\
  Professional\Team Tools\Static Analysis Tools\\Rule Sets\MinimumRecommendedRules.ruleset&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/subsystemversion:6.00&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/tar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get:exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warnaserror-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/utf8output&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/deterministic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Program.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ed.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\obj\Debug\net48\Demo.g.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj\Debug\net48\Walterlv.GettingSta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rted.SourceYard.Sample_vobqk5lg_wpftmp.AssemblyInfo.cs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/warnaserror&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NU1605&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;对来自后列目录的编译器使用共享编译&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Studio\2019\Professional\MSBuild\Current\Bin\Roslyn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Program.cs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CS0234:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;命名空间“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”中不存在类型或命名空间名“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SourceYard&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;是否缺少程序集引用?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Walterlv.GettingStarted.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;SourceYard&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.SourceYard.Sample_&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;vobqk5lg_wpftmp.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已完成生成项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Walterlv.GettingStarted.SourceYard.Sample_vobqk5lg_wpftmp.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CompileTemporaryAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的操作&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;失败。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已完成生成项目“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;\Walterlv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的操作&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;失败。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;


&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成失败。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\Walter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lv.GettingStarted.SourceYard.Sample.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\Walter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lv.GettingStarted.SourceYard.Sample_vobqk5lg_wpftmp.csproj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_CompileTemporaryAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CoreCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;目标&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Program.cs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CS0234:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;命名空间“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”中不存在类型或命名空间名“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SourceYard&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;是否缺少程序集引用?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Developments\Open\Walterlv.Dem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;o\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.SourceYard.Sampl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;e_vobqk5lg_wpftmp.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个警告&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;个错误&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;已用时间&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;00.87&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为上面有编译错误但看不出来，所以我们贴一张图，可以很容易看出来有编译错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-10-17-44-40.png&quot; alt=&quot;出现编译错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;并且，如果对比两张图，会发现 CoreCompile 中的内容已经不一样了。变化主要是 &lt;code class=&quot;highlighter-rouge&quot;&gt;/reference&lt;/code&gt; 参数和要编译的文件列表参数。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/reference&lt;/code&gt; 参数增加了 WPF 需要的库。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    mscorelib.dll
&lt;span class=&quot;gi&quot;&gt;++  PresentationCore.dll
++  PresentationFramework.dll
&lt;/span&gt;    System.Core.dll
    System.Data.dll
    System.dll
    System.Drawing.dll
    System.IO.Compression.FileSystem.dll
    System.Numerics.dll
    System.Runtime.Serialization.dll
&lt;span class=&quot;gi&quot;&gt;++  System.Windows.Controls.Ribbon.dll
++  System.Xaml.dll
&lt;/span&gt;    System.Xml.dll
    System.Xml.Linq.dll
&lt;span class=&quot;gi&quot;&gt;++  UIAutomationClient.dll
++  UIAutomationClientsideProviders.dll
++  UIAutomationProvider.dll
++  UIAutomationTypes.dll
++  WindowsBase.dll
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是要编译的文件却既有新增，又有减少：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    Program.cs
&lt;span class=&quot;gi&quot;&gt;++  D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\obj\Debug\net48\Demo.g.cs
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--  &quot;C:\Users\lvyi\AppData\Local\Temp\.NETFramework,Version=v4.8.AssemblyAttributes.cs&quot;
--  C:\Users\lvyi\.nuget\packages\walterlv.sourceyard.demo\0.1.0-alpha\build\..\src\Foo.cs
--  obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample.AssemblyInfo.cs
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  obj\Debug\net48\Walterlv.GettingStarted.SourceYard.Sample_vobqk5lg_wpftmp.AssemblyInfo.cs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同时，我们还能注意到还临时生成了一个新的项目文件：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;项目“D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.SourceYard.Sample.csproj”(1)正在节点 1 上生成“D:\Walterlv.Demo\Walterlv.GettingStarted.SourceYard.Sample\Walterlv.GettingStarted.SourceYard.Sample_vobqk5lg_wpftmp.csproj”(2) (_CompileTemporaryAssembly 个目标)。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;新的项目文件有一个后缀 &lt;code class=&quot;highlighter-rouge&quot;&gt;_vobqk5lg_wpftmp&lt;/code&gt;，同时我们还能注意到编译的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyInfo.cs&lt;/code&gt; 文件前面也有相同的后缀 &lt;code class=&quot;highlighter-rouge&quot;&gt;_vobqk5lg_wpftmp&lt;/code&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$(项目名)_$(随机字符)_wpftmp.csproj&lt;/li&gt;
  &lt;li&gt;$(项目名)_$(随机字符)_wpftmp.AssemblyInfo.cs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们几乎可以认为，当项目是编译成 WPF 时，执行了不同的编译流程。&lt;/p&gt;

&lt;h2 id=&quot;修复错误&quot;&gt;修复错误&lt;/h2&gt;

&lt;h3 id=&quot;找出原因&quot;&gt;找出原因&lt;/h3&gt;

&lt;p&gt;要了解问题到底出在哪里了，我们需要知道 WPF 究竟在编译过程中做了哪些额外的事情。WPF 额外的编译任务主要在 Microsoft.WinFX.targets 文件中。在了解了 WPF 的编译过程之后，这个临时的程序集将非常容易理解。&lt;/p&gt;

&lt;p&gt;我写了一篇讲解 WPF 编译过程的博客，在解决这个问题之前，建议阅读这篇博客了解 WPF 是如何进行编译的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-wpf-assemblies-are-compiled&quot;&gt;WPF 程序的编译过程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在了解了 WPF 程序的编译过程之后，我们知道了前面一些疑问的答案：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;那个临时的项目文件是如何生成的；&lt;/li&gt;
  &lt;li&gt;那个临时项目文件和原始的项目文件有哪些不同；&lt;/li&gt;
  &lt;li&gt;编译临时项目文件时，哪些编译目标会执行，哪些编译目标不会执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在那篇博客中，我们解释到新生成的项目文件会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt; 替代其他方式收集到的引用，这就包含项目引用和 NuGet 包的引用。&lt;/p&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt; 的情况下，无论是项目引用还是 NuGet 包引用，都会被换成普通的 dll 引用，因为这个时候目标项目都已经编译完成，包含可以被引用的程序集。&lt;/p&gt;

&lt;p&gt;以下是我在示例程序中抓取到的临时生成的项目文件的内容，与原始项目文件之间的差异：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
        &amp;lt;PropertyGroup&amp;gt;
            &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
            &amp;lt;TargetFramework&amp;gt;net48&amp;lt;/TargetFramework&amp;gt;
            &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
            &amp;lt;GenerateTemporaryTargetAssemblyDebuggingInformation&amp;gt;True&amp;lt;/GenerateTemporaryTargetAssemblyDebuggingInformation&amp;gt;
        &amp;lt;/PropertyGroup&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
            &amp;lt;PackageReference Include=&quot;Walterlv.SourceYard.Demo&quot; Version=&quot;0.1.0-alpha&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;ItemGroup&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationCore.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationFramework.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Core.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Drawing.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Compression.FileSystem.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Numerics.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Runtime.Serialization.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Controls.Ribbon.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xaml.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.Linq.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClient.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClientsideProviders.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationProvider.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationTypes.dll&quot; /&amp;gt;
++          &amp;lt;ReferencePath Include=&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsBase.dll&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
++      &amp;lt;ItemGroup&amp;gt;
++          &amp;lt;Compile Include=&quot;D:\Developments\Open\Walterlv.Demo\Walterlv.GettingStarted.SourceYard\Walterlv.GettingStarted.SourceYard.Sample\obj\Debug\net48\Demo.g.cs&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能已经注意到了我在项目中设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssemblyDebuggingInformation&lt;/code&gt; 属性，这个属性可以让 WPF 临时生成的项目文件保留下来，便于进行研究和调试。在前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 的源码部分我们已经贴出了这个属性使用的源码，只是前面我们没有说明其用途。&lt;/p&gt;

&lt;p&gt;注意，虽然新生成的项目文件中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 来表示包引用，但由于只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 指定的编译目标和相关依赖可以被执行，而 NuGet 包中自动 Import 的部分没有加入到依赖项中，所以实际上包中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件都不会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 进来，这可能造成部分 NuGet 包在 WPF 项目中不能正常工作。比如本文正片文章都在探索的这个 Bug。&lt;/p&gt;

&lt;p&gt;更典型的，就是 SourceYard 项目，这个 Bug 给 SourceYard 造成了不小的困扰：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.GettingStarted.SourceYard&quot;&gt;walterlv.demo/Walterlv.GettingStarted.SourceYard at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;解决问题&quot;&gt;解决问题&lt;/h3&gt;

&lt;p&gt;这个问题解决起来其实并不如想象当中那么简单，因为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;WPF 项目的编译包含两个编译上下文，一个是正常的编译上下文，另一个是临时生成的项目文件编译的上下文；正常的编译上下文编译到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass2&lt;/code&gt; 之间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 编译目标时，会插入一段临时项目文件的编译；&lt;/li&gt;
  &lt;li&gt;临时项目文件的编译中，会执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 内部属性指定的编译目标，虽然相当于开放了修改，但由于临时项目文件中不会执行 NuGet 相关的编译目标，所以不会自动 Import NuGet 包中的任何编译目标和属性定义；换句话说，我们几乎没有可以自动 Import 源码的方案。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果我们强行将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_CompileTargetNameForLocalType&lt;/code&gt; 替换成我们自己定义的类型会怎么样？&lt;/p&gt;

&lt;p&gt;这是通过 NuGet 包中的 .targets 文件中的内容，用来强行替换：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuildAllProjects&amp;gt;&lt;/span&gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MSBuildAllProjects&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_CompileTargetNameForLocalType&amp;gt;&lt;/span&gt;_WalterlvCompileTemporaryAssembly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_CompileTargetNameForLocalType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvCompileTemporaryAssembly&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在属性中将临时项目的编译目标改成了我们自己的目标，但会直接出现编译错误，找不到我们定义的编译目标。当然这个编译错误出现在临时生成的程序集上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-14-21-48.png&quot; alt=&quot;编译错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原因就在于这个 .targets 文件没有自动被 Import 进来，于是我们定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvCompileTemporaryAssembly&lt;/code&gt; 在临时生成的项目编译中根本就不存在。&lt;/p&gt;

&lt;p&gt;我们失去了通过 NuGet 自动被 Import 的时机！&lt;/p&gt;

&lt;p&gt;既然我们失去了通过 NuGet 被自动 Import 的时机，那么我们只能另寻它法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;帮助微软修复 NuGet 在 WPF 临时生成的项目中依然可以自动 Import 编译文件 .props 和 .targets；&lt;/li&gt;
  &lt;li&gt;直接修改项目文件，使其直接或间接 Import 我们希望 Import 进来的编译文件 .props 和 .targets。&lt;/li&gt;
  &lt;li&gt;寻找其他可以被自动 Import 的时机进行自动 Import；&lt;/li&gt;
  &lt;li&gt;不管时机了，从 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 这个编译任务入手，修改其需要的参数；&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;方案一帮助微软修复等待中&quot;&gt;方案一：帮助微软修复（等待中）&lt;/h4&gt;

&lt;p&gt;// TODO：正在组织 issues 和 pull request&lt;/p&gt;

&lt;p&gt;无论结果如何，等待微软将这些修改发布也是需要一段时间的，这段时间我们需要使用方案二和方案三来顶替一段时间。&lt;/p&gt;

&lt;h4 id=&quot;方案二修改项目文件可行但不好&quot;&gt;方案二：修改项目文件（可行，但不好）&lt;/h4&gt;

&lt;p&gt;方案二的其中一种实施方案是下面这篇文章在最后一小节说到的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-microsoft.net.sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85#%E8%A7%A3%E5%86%B3-xaml-%E6%89%BE%E4%B8%8D%E5%88%B0%E6%96%B9%E6%B3%95%E9%97%AE%E9%A2%98&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体来说，就是修改项目文件，在项目文件的首尾各加上 NuGet 自动生成的那些 Import 来自 NuGet 中的所有编译文件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('obj\$(MSBuildProjectName).csproj.nuget.g.props') &quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj\$(MSBuildProjectName).csproj.nuget.g.props&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 项目文件中的原有其他代码。 --&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('obj\$(MSBuildProjectName).csproj.nuget.g.targets') &quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj\$(MSBuildProjectName).csproj.nuget.g.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，可以直接在这里 Import 我们 NuGet 包中的编译文件，但这些不如以上方案来得靠谱，因为上面的代码可以使得项目文件的修改完全确定，不用随着开发计算机的不同或者 NuGet 包的数量和版本不同而变化。&lt;/p&gt;

&lt;p&gt;如果打算选用方案二，那么上面这种实施方式是最推荐的实施方式。&lt;/p&gt;

&lt;p&gt;当然需要注意，此方案的副作用是会多出重复导入的编译警告。在清楚了 WPF 的编译过程之后，是不是能理解了这个警告的原因了呢？是的，对临时项目来说，由于没有自动 Import，所以这里的 Import 不会导致临时项目出现问题；但对于原项目来说，由于默认就会 Import NuGet 中的那两个文件，所以如果再次 Import 就会重复导入。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-14-42-19.png&quot; alt=&quot;重复导入的编译警告&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;方案三寻找其他自动-import-的时机不可行&quot;&gt;方案三：寻找其他自动 Import 的时机（不可行）&lt;/h4&gt;

&lt;p&gt;Directory.Build.props 和 Directory.Build.targets 也是可以被自动 Import 的文件，这也是在 Microsoft.NET.Sdk 中将其自动导入的。&lt;/p&gt;

&lt;p&gt;关于这两个文件的自动导入，可以阅读博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-%E4%BD%BF%E7%94%A8-directory.build.props-%E6%96%87%E4%BB%B6%E5%AE%9A%E4%B9%89%E7%BC%96%E8%AF%91&quot;&gt;Roslyn 使用 Directory.Build.props 文件定义编译&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，如果我们使用这两个文件帮助自动导入，将造成导入循环，这会形成编译错误！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-14-54-57.png&quot; alt=&quot;因导入循环造成的编译错误&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;方案四设置-generatetemporarytargetassembly-编译任务&quot;&gt;方案四：设置 GenerateTemporaryTargetAssembly 编译任务&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;GenerateTemporaryTargetAssembly&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;CurrentProject=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildProjectFullPath)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;MSBuildBinPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildBinPath)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ReferencePathTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferencePath&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;CompileTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Compile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;GeneratedCodeFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_GeneratedCodeFiles)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ReferencePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(ReferencePath)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;IntermediateOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;AssemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AssemblyName)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;CompileTargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_CompileTargetNameForLocalType)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;GenerateTemporaryTargetAssemblyDebuggingInformation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(GenerateTemporaryTargetAssemblyDebuggingInformation)&quot;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GenerateTemporaryTargetAssembly&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到它的的参数有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CurrentProject，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectFullPath)&lt;/code&gt;，表示项目文件的完全路径，修改无效。&lt;/li&gt;
  &lt;li&gt;MSBuildBinPath，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;，表示 MSBuild 程序的完全路径，修改无效。&lt;/li&gt;
  &lt;li&gt;ReferencePathTypeName，传入了字符串常量 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt;，这是为了在生成临时项目文件时使用正确的引用路径项的名称。&lt;/li&gt;
  &lt;li&gt;CompileTypeName，传入了字符串常量 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt;，这是为了在生成临时项目文件时使用正确的编译项的名称。&lt;/li&gt;
  &lt;li&gt;GeneratedCodeFiles，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_GeneratedCodeFiles)&lt;/code&gt;，包含生成的代码文件，也就是那些 .g.cs 文件。&lt;/li&gt;
  &lt;li&gt;ReferencePath，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(ReferencePath)&lt;/code&gt;，也就是目前已收集到的所有引用文件的路径。&lt;/li&gt;
  &lt;li&gt;IntermediateOutputPath，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)&lt;/code&gt;，表示临时输出路径，当使用临时项目文件编译时，生成的临时程序集将放在这个目录中。&lt;/li&gt;
  &lt;li&gt;AssemblyName，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(AssemblyName)&lt;/code&gt;，表示程序集名称，当生成临时程序集的时候，将参考这个程序集名称。&lt;/li&gt;
  &lt;li&gt;CompileTargetName，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(_CompileTargetNameForLocalType)&lt;/code&gt;，表示当生成了新的项目文件后，要使用哪个编译目标来编译这个项目。&lt;/li&gt;
  &lt;li&gt;GenerateTemporaryTargetAssemblyDebuggingInformation，传入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(GenerateTemporaryTargetAssemblyDebuggingInformation)&lt;/code&gt;，表示是否要为了调试保留临时生成的项目文件和程序集。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可能为我们所用的有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_GeneratedCodeFiles)&lt;/code&gt;，我们可以把我们需要 Import 进来的源代码伪装成生成的 .g.cs 文件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;好吧，就这一个了。其他的并不会对我们 Import 源代码造成影响。&lt;/p&gt;

&lt;p&gt;于是回到我们本文一开始的 Walterlv.SourceYard.Demo.targets 文件，我们将内容修改一下，增加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_ENSdkImportInTempProject&lt;/code&gt; 编译目标。它在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupCompilePass1&lt;/code&gt; 之后执行，因为这是 XAML 的第一轮编译，会创造 &lt;code class=&quot;highlighter-rouge&quot;&gt;_GeneratedCodeFiles&lt;/code&gt; 这个集合，将 XAML 生成 .g.cs 文件；在 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateTemporaryTargetAssembly&lt;/code&gt; 之前执行，因为这里会生成一个新的临时项目，然后立即对其进行编译。我们选用这个之间的时机刚好可以在产生 &lt;code class=&quot;highlighter-rouge&quot;&gt;_GeneratedCodeFiles&lt;/code&gt; 集合之后修改其内容。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project&amp;gt;

      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;MSBuildAllProjects&amp;gt;$(MSBuildAllProjects);$(MSBuildThisFileFullPath)&amp;lt;/MSBuildAllProjects&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;

      &amp;lt;Target Name=&quot;_WalterlvIncludeSomeCode&quot; BeforeTargets=&quot;CoreCompile&quot;&amp;gt;
        &amp;lt;ItemGroup&amp;gt;
          &amp;lt;Compile Include=&quot;$(MSBuildThisFileDirectory)..\src\Foo.cs&quot; /&amp;gt;
        &amp;lt;/ItemGroup&amp;gt;
      &amp;lt;/Target&amp;gt;
      
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;Target Name=&quot;_ENSdkImportInTempProject&quot; AfterTargets=&quot;MarkupCompilePass1&quot; BeforeTargets=&quot;GenerateTemporaryTargetAssembly&quot;&amp;gt;
++      &amp;lt;ItemGroup&amp;gt;
++        &amp;lt;_GeneratedCodeFiles Include=&quot;$(MSBuildThisFileDirectory)..\src\Foo.cs&quot; /&amp;gt;
++      &amp;lt;/ItemGroup&amp;gt;
++    &amp;lt;/Target&amp;gt;
++    
&lt;/span&gt;    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在重新再编译，我们本文一开始疑惑的各种问题，现在终于无警告无错误地解决掉了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-11-15-25-50.png&quot; alt=&quot;解决掉的源代码包问题&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决关键&quot;&gt;解决关键&lt;/h2&gt;

&lt;p&gt;如果你觉得本文略长，希望立刻获得解决办法，可以：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;直接使用 “方案四” 中新增的那一段代码；&lt;/li&gt;
  &lt;li&gt;阅读我的另一篇专门的只说解决方案的博客：&lt;a href=&quot;/post/build-source-code-nuget-package-for-wpf-projects&quot;&gt;如何为 WPF 项目制作源代码包（SourceYard 基础原理篇，解决 WPF 项目编译问题和 NuGet 包中的各种问题）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/210156/msbuild-is-adding-a-random-hash-and-wpftmp-to-my-a.html&quot;&gt;msbuild is adding a random hash and wpftmp to my AssemblyName during build - Developer Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/wpf-dot-targets-files&quot;&gt;WPF .Targets Files - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/markupcompilepass2-task&quot;&gt;MarkupCompilePass2 Task - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 11 Jun 2019 07:30:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/issues-of-nuget-package-import-for-wpf-projects.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/issues-of-nuget-package-import-for-wpf-projects.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>nuget</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>MSBuild 在编写编译任务的时候判断当前是否在 Visual Studio 中编译</title>
        <description>&lt;p&gt;我们这里说的编译任务是 MSBuild 的 Target。虽然只有少部分，但确实有一些情况需要判断是否在 Visual Studio 中编译的时候才需要执行的编译任务，典型的如某些仅为设计器准备的代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文需要理解的前置知识是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而使用 Visual Studio 编译的时候，会自动帮我们设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;BuildingInsideVisualStudio&lt;/code&gt; 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;，所以实际上我们可以使用这个值进行判断。&lt;/p&gt;

&lt;p&gt;我们可以在 Microsoft.NET.Sdk 中找到不少使用此属性的编译任务。&lt;/p&gt;

&lt;p&gt;比如为了 IO 性能考虑的硬连接，在 Visual Studio 中即便打开也不会使用：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--
  ============================================================
                                      CopyFilesToOutputDirectory

  Copy all build outputs, satellites and other necessary files to the final directory.
  ============================================================
  --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- By default we're not using Hard or Symbolic Links to copy to the output directory, and never when building in VS --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateHardLinksForCopyAdditionalFilesIfPossible&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(BuildingInsideVisualStudio)' == 'true' or '$(CreateHardLinksForCopyAdditionalFilesIfPossible)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateHardLinksForCopyAdditionalFilesIfPossible&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateSymbolicLinksForCopyAdditionalFilesIfPossible&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(BuildingInsideVisualStudio)' == 'true' or '$(CreateSymbolicLinksForCopyAdditionalFilesIfPossible)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateSymbolicLinksForCopyAdditionalFilesIfPossible&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外 Visual Studio 接管了一部分引用项目的清理工作，所以编译任务里面也将其过滤掉了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--
  ============================================================
                                      CleanReferencedProjects

  Call Clean target on all Referenced Projects.
  ============================================================
  --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CleanReferencedProjects&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PrepareProjectReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!--
      When building the project directly from the command-line, clean those referenced projects
      that exist on disk.  For IDE builds and command-line .SLN builds, the solution build manager
      takes care of this.
      --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;MSBuild&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Projects=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_MSBuildProjectReferenceExistent)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Clean&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Properties=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(_MSBuildProjectReferenceExistent.SetConfiguration); %(_MSBuildProjectReferenceExistent.SetPlatform); %(_MSBuildProjectReferenceExistent.SetTargetFramework)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;BuildInParallel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BuildInParallel)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(BuildingInsideVisualStudio)' != 'true' and '$(BuildProjectReferences)' == 'true' and '@(_MSBuildProjectReferenceExistent)' != ''&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ContinueOnError=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ContinueOnError)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RemoveProperties=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于如何探索 Microsoft.NET.Sdk 可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 04 Jun 2019 14:23:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/determine-building-in-visual-studio-during-building.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/determine-building-in-visual-studio-during-building.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>git 配置错误导致无法推送远端仓库？本文介绍各种修复方式</title>
        <description>&lt;p&gt;无论你使用原生的 git 命令行，还是使用其他的 GUI 客户端来管理你的 git 仓库，都会遇到 git 远程仓库的身份认证机制。如果在某个远程仓库第一次认证的时候输入了错误的信息，那么 git 以及一部分 git GUI 客户端会记住这个错误的身份认证信息，使得以后也不能继续与远程仓库进行交互了。&lt;/p&gt;

&lt;p&gt;本文介绍如何清除 git 的身份认证信息，以便你可以重新获得输入正确身份认证的机会。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;凭据管理器&quot;&gt;凭据管理器&lt;/h2&gt;

&lt;p&gt;如果你使用基于 https 的身份认证方式操作 git 远端，并且输入了错误的密码，那么这部分密码将保存在 Windows 的凭据管理器中。&lt;/p&gt;

&lt;p&gt;在 Windows 搜索框中搜索“凭据管理器”或者在控制面板中进入“用户账户”-&amp;gt;“凭据管理器”可以打开凭据管理界面。我们需要选择右边的“Windows 凭据”标签。&lt;/p&gt;

&lt;p&gt;随后，在下方的“普通凭据”中，找到出现问题的 git 远程仓库地址，然后展开，将其删除。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-14-39-41.png&quot; alt=&quot;凭据管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;删除之后，再次在 git 命令行或者基于 git 命令行的客户端的 GUI 客户端中使用 git 操作远端仓库将会重新提示输入这个远端仓库的用户名和密码。&lt;/p&gt;

&lt;h2 id=&quot;ssh&quot;&gt;.ssh&lt;/h2&gt;

&lt;p&gt;基于 SSH 的身份认证方式需要自己手工方式都是需要自己手动配置好才可以正常使用的，不会给你像 https 那样输错密码的机会。如果配置错误则不能操作远端仓库。当然，配错了直接删掉重新再来一次就好了。参见网上一大堆的配置方法：&lt;a href=&quot;https://segmentfault.com/a/1190000002645623&quot;&gt;git-ssh 配置和使用 - fedl - SegmentFault 思否&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-14-57-50.png&quot; alt=&quot;配置好的 SSH&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;tortoisegitplink&quot;&gt;TortoiseGitPlink&lt;/h2&gt;

&lt;p&gt;另外，有一些客户端如 Tortoise 会自带一份认证管理工具。TortoiseGit 自带了 TortoiseGitPlink，它声称比自带的 SSH 要好用但问题是你得单独为它配置一遍……（逃&lt;/p&gt;

&lt;p&gt;命名 SSH 配好了而没有配 TortoiseGitPlink 的时候，它分分钟挂给你看：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-14-55-01.png&quot; alt=&quot;TortoiseGitPlink&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么如何修复呢？&lt;/p&gt;

&lt;h3 id=&quot;方法一替换-ssh-客户端&quot;&gt;方法一：替换 SSH 客户端&lt;/h3&gt;

&lt;p&gt;替换为与 git 命令行相同的 SSH 客户端可以避免重复配置公私钥对。&lt;/p&gt;

&lt;p&gt;打开 TortoiseGit 的设置页面，切换到“网络”标签，然后将 SSH 客户端改为 SSH。通常在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\Git\usr\bin&lt;/code&gt; 目录中，如果没找到，也可以去 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Git\bin\ssh.exe&lt;/code&gt; 目录寻找。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-15-22-34.png&quot; alt=&quot;SSH 客户端&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;方法二导入已有的-ssh-配置&quot;&gt;方法二：导入已有的 SSH 配置&lt;/h3&gt;

&lt;p&gt;打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\TortoiseGit\bin\puttygen.exe&lt;/code&gt; 程序，然后点击“Load”，选择 git 客户端早已配好的 ssh 私钥。如果打开文件对话框中你找不到密钥文件，可能需要将过滤器设置为所有文件（&lt;code class=&quot;highlighter-rouge&quot;&gt;*.*&lt;/code&gt;）。（如果之前没配好 SSH，那么建议去配置一下，不然 SSH 的认证方式将只有 TortoiseGit 客户端工具可用。本节接下来的内容将默认你已经配好 SSH，在远端仓库添加了公钥。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-15-13-15.png&quot; alt=&quot;puttygen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-15-17-17.png&quot; alt=&quot;导入成功&quot; /&gt;&lt;/p&gt;

&lt;p&gt;导入成功之后，点击保存私钥，选择一个合适的路径存下来。&lt;/p&gt;

&lt;p&gt;随后，打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\TortoiseGit\bin\puttygen.exe&lt;/code&gt; 程序。打开之后，你会在任务栏通知区域看到它的图标，右键点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add Key&lt;/code&gt; 然后选择我们刚刚保存的私钥。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-04-15-25-17.png&quot; alt=&quot;Add Key&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，你需要保持 &lt;code class=&quot;highlighter-rouge&quot;&gt;puttygen.exe&lt;/code&gt; 一直处于运行状态，以便 TortoiseGit 可以一直使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/31782500/6233938&quot;&gt;git - Remove saved credentials from TortoiseGit - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/39944557/6233938&quot;&gt;git - my old username is still in use - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/28106717/how-to-solve-tortoisegitplink-fatal-error&quot;&gt;windows - How to solve TortoiseGitPlink Fatal Error? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 04 Jun 2019 07:31:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-credential-issues-of-git.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-credential-issues-of-git.html</guid>
        
        
        <category>git</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>使用 ProcessMonitor 找到进程所操作的文件的路径</title>
        <description>&lt;p&gt;很多系统问题都是可以修的，不需要重装系统，但是最近我还是重装了。发现之前正在玩的一款游戏的存档没有了……因为我原有系统的数据并没有删除，所以我还是能找回原来的游戏存档的。但是，我怎么知道这款游戏将存档放在了那个路径下呢？搜索当然是好方法，不过我喜欢玩的游戏大多是冷门游戏，有些搜不到。于是我就用 Process Monitor 找到了存档所在，恢复了我的游戏进度。&lt;/p&gt;

&lt;p&gt;本文介绍如何使用 ProcessMonitor 找出进程创建和修改的文件路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载-process-monitor&quot;&gt;下载 Process Monitor&lt;/h2&gt;

&lt;p&gt;Process Monitor 是微软极品工具箱的一部分，你可以在此页面下载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/sysinternals/downloads/procmon&quot;&gt;Process Monitor - Windows Sysinternals - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;打开-process-monitor&quot;&gt;打开 Process Monitor&lt;/h2&gt;

&lt;p&gt;当你一开始打开 Process Monitor 的时候，列表中会立刻刷出大量的进程的操作记录。这么多的记录会让我们找到目标进程操作的文件有些吃力，于是我们需要设置规则。&lt;/p&gt;

&lt;p&gt;Process Monitor 的工具栏按钮并不多，而且我们这一次的目标只会用到其中的两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;清除列表（将已经记录的所有数据清空，便于聚焦到我们最关心的数据中）&lt;/li&gt;
  &lt;li&gt;设置过滤器（防止大量无关的进程操作进入列表中干扰我们的查找）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-01-13-36-35.png&quot; alt=&quot;Process Monitor 的工具栏按钮&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;设置过滤规则&quot;&gt;设置过滤规则&lt;/h2&gt;

&lt;p&gt;我启动了我想要玩的游戏，在任务管理器中发现它的进程名称是 RIME.exe。呃……如果你也想玩，给你个链接：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.epicgames.com/store/en-US/product/rime/home&quot;&gt;RiME - Explore the beautiful yet rugged world of RiME&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;点击设置过滤规则按钮，可以看到下面的界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-01-13-40-45.png&quot; alt=&quot;设置过滤器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以选定 &lt;code class=&quot;highlighter-rouge&quot;&gt;某个名词&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;与另一个字符串&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;进行某种操作&lt;/code&gt; 之后 &lt;code class=&quot;highlighter-rouge&quot;&gt;引入 (Include)&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;排除 (Exclude)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我希望找到 RIME 这款游戏的游戏存档位置，所以我需要进入游戏，玩到第一个会存档的地方之后观察监视的操作记录。&lt;/p&gt;

&lt;p&gt;所以我希望的过滤器规则是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将所有不是 RIME.exe 进程的记录全部排除；&lt;/li&gt;
  &lt;li&gt;将不是文件操作的记录全部排除；&lt;/li&gt;
  &lt;li&gt;将读文件的记录排除（这样剩下的只会是写文件，毕竟游戏读文件很频繁的）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是我设置了这些规则：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[ProcessName] is [RIME.exe]      then [Exclude]
[Operation]   is [RegOpenKey]    then [Exclude]
[Operation]   is [RegCloseKey]   then [Exclude]
[Operation]   is [RegQueryKey]   then [Exclude]
[Operation]   is [RegQueryValue] then [Exclude]
[Operation]   is [RegEnumKey]    then [Exclude]
[Operation]   is [RegSetInfoKey] then [Exclude]
[Operation]   is [ReadFile]      then [Exclude]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，剩下的记录将主要是文件写入以及一些不常见的操作了。&lt;/p&gt;

&lt;h2 id=&quot;分析记录&quot;&gt;分析记录&lt;/h2&gt;

&lt;p&gt;现在，我在游戏里面玩到了第一个存档点，终于在 Process Monitor 的进程列表中看到了创建文件和写入文件相关的操作了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-06-01-13-47-11.png&quot; alt=&quot;记录的列表&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过观察 Path 的值，我可以知道 RIME 游戏的存档放在了 &lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%\SirenGame&lt;/code&gt; 文件夹下。&lt;/p&gt;

&lt;p&gt;于是我关掉 RIME 游戏，将原来系统中的此文件夹覆盖到新系统中的此文件夹之后，再次打开游戏，我恢复了我的全部游戏存档了。&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Jun 2019 05:49:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/find-process-operated-files-using-process-monitor.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/find-process-operated-files-using-process-monitor.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何为 Win32 的打开和保存对话框编写文件过滤器（Filter）</title>
        <description>&lt;p&gt;在使用 Win32 / WPF / Windows Forms 的打开或保存文件对话框的时候，多数情况下我们都会考虑编写文件过滤器。UWP 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileTypeFilter&lt;/code&gt; 集合可以添加不同的文件种类，但 Win32 中却是一个按一定规则组合而成的字符串。&lt;/p&gt;

&lt;p&gt;因为其包含一定的格式，所以可能写错。本文介绍如何编写 Filter。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写-filter&quot;&gt;编写 Filter&lt;/h2&gt;

&lt;p&gt;Filter 使用竖线分隔不同种类的过滤器，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;图片|*.png;*.jpg|文本|*.txt|walterlv 的自定义格式|*.lvyi&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dialog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OpenFileDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;图片|*.png;*.jpg|文本|*.txt|walterlv 的自定义格式|*.lvyi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-31-20-31-40.png&quot; alt=&quot;过滤器的显示效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有时我们会看到一些程序的过滤器里面显示了过滤器本身，而不止是名称，实际上是因为名称中包含了过滤器：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;图片 (png, jpg)|*.png;*.jpg|文本 (txt)|*.txt|walterlv 的自定义格式 (lvyi)|*.lvyi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-31-20-34-49.png&quot; alt=&quot;名称中包含过滤器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你不可以在过滤器中省略名称或者过滤器任何一个部分，否则会抛出异常。&lt;/p&gt;

&lt;h2 id=&quot;附如何显示对话框&quot;&gt;附：如何显示对话框&lt;/h2&gt;

&lt;p&gt;对于 .NET Core 版本的 WPF 或者 Windows Forms 程序来说，需要安装 Windows 兼容 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Windows.Compatibility&quot;&gt;Microsoft.Windows.Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安装后可以使用 Windows Forms 版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OpenFileDialog&lt;/code&gt; 或者 WPF 版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Win32.OpenFileDialog&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/microsoft.win32.filedialog.filter&quot;&gt;FileDialog.Filter Property (Microsoft.Win32) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 31 May 2019 12:36:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-write-win32-file-dialog-filter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-write-win32-file-dialog-filter.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>将基于 .NET Framework 的 WPF 项目迁移到基于 .NET Core 3</title>
        <description>&lt;p&gt;在 Connect(); 2018 大会上，微软发布了 .NET Core 3 Preview，以及基于 .NET Core 3 的 WPF；同时还发布了 Visual Studio 2019 预览版。你可以基于 .NET Core 3 创建 WPF 程序。不过，如果你已经有基于 .NET Framework 的 WPF 项目，那么如何快速迁移到基于 .NET Core 的版本呢？&lt;/p&gt;

&lt;p&gt;本文将指导大家将现有基于 .NET Framework 的 WPF 项目迁移到基于 .NET Core 3 的版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-net-core-30-preview-sdk&quot;&gt;安装 .NET Core 3.0 Preview SDK&lt;/h2&gt;

&lt;p&gt;前往官网下载：&lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-core/3.0&quot;&gt;.NET Core 3.0 downloads for Linux, macOS, and Windows&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;然后安装。&lt;/p&gt;

&lt;p&gt;如果你没有安装 Visual Studio 2019 Preview，请前往下载：&lt;a href=&quot;https://visualstudio.microsoft.com/vs/preview/&quot;&gt;Visual Studio 2019&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;编辑-csproj-文件&quot;&gt;编辑 csproj 文件&lt;/h2&gt;

&lt;p&gt;卸载你原有的 WPF 项目，然后右键“编辑 csproj 文件”。将里面所有的内容改为以下代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseWPF&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseWPF&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的项目是 Exe，则设为 WinExe；如果是 WPF 类库，则删掉这一行 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;WinExe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的原有项目中有 App.manifest 文件，则在此加入 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;ApplicationManifest&amp;gt;Properties\App.manifest&amp;lt;/ApplicationManifest&amp;gt; --&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的原有项目中有 App.ico 图标，则在此加入 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;ApplicationIcon&amp;gt;Properties\App.ico&amp;lt;/ApplicationIcon&amp;gt; --&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的原有项目中有自定义的 Main 函数，则在此加入 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;StartupObject&amp;gt;Walterlv.Whitman.Program&amp;lt;/StartupObject&amp;gt; --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的原有项目中有自己添加的图标文件，则在此加入 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\App.ico&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的原有项目中有其他非 .cs、.xaml 文件，则需要在这里加入 --&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;编辑-assemblyinfocs-文件&quot;&gt;编辑 AssemblyInfo.cs 文件&lt;/h2&gt;

&lt;p&gt;由于在 .NET Core 中，程序集相关的信息是自动生成的，所以原有 AssemblyInfo.cs 中的大量程序集信息是需要删掉的，不然会出现重复 Attribute 的错误。&lt;/p&gt;

&lt;p&gt;看以下代码，红色标记 “–” 的代码是需要删掉的，其他的代码保留。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  using System.Reflection;
--  using System.Resources;
--  using System.Runtime.CompilerServices;
&lt;/span&gt;    using System.Runtime.InteropServices;
    using System.Windows;
    
&lt;span class=&quot;gd&quot;&gt;--  // General Information about an assembly is controlled through the following
--  // set of attributes. Change these attribute values to modify the information
--  // associated with an assembly.
--  [assembly: AssemblyTitle(&quot;Whitman&quot;)]
--  [assembly: AssemblyDescription(&quot;&quot;)]
--  [assembly: AssemblyConfiguration(&quot;&quot;)]
--  [assembly: AssemblyCompany(&quot;&quot;)]
--  [assembly: AssemblyProduct(&quot;Whitman&quot;)]
--  [assembly: AssemblyCopyright(&quot;Copyright © walterlv 2018&quot;)]
--  [assembly: AssemblyTrademark(&quot;&quot;)]
--  [assembly: AssemblyCulture(&quot;&quot;)]
--  
&lt;/span&gt;    // Setting ComVisible to false makes the types in this assembly not visible
    // to COM components.  If you need to access a type in this assembly from
    // COM, set the ComVisible attribute to true on that type.
    [assembly: ComVisible(false)]
    
&lt;span class=&quot;gd&quot;&gt;--  //In order to begin building localizable applications, set
--  //&amp;lt;UICulture&amp;gt;CultureYouAreCodingWith&amp;lt;/UICulture&amp;gt; in your .csproj file
--  //inside a &amp;lt;PropertyGroup&amp;gt;.  For example, if you are using US english
--  //in your source files, set the &amp;lt;UICulture&amp;gt; to en-US.  Then uncomment
--  //the NeutralResourceLanguage attribute below.  Update the &quot;en-US&quot; in
--  //the line below to match the UICulture setting in the project file.
--  
--  //[assembly: NeutralResourcesLanguage(&quot;en-US&quot;, UltimateResourceFallbackLocation.Satellite)]
--  
--  
&lt;/span&gt;    [assembly: ThemeInfo(
        ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
                                         //(used if a resource is not found in the page,
                                         // or application resource dictionaries)
        ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
                                                  //(used if a resource is not found in the page,
                                                  // app, or any theme specific resource dictionaries)
    )]
&lt;span class=&quot;gd&quot;&gt;--  
--  
--  // Version information for an assembly consists of the following four values:
--  //
--  //      Major Version
--  //      Minor Version
--  //      Build Number
--  //      Revision
--  //
--  // You can specify all the values or you can default the Build and Revision Numbers
--  // by using the '*' as shown below:
--  // [assembly: AssemblyVersion(&quot;1.0.*&quot;)]
--  [assembly: AssemblyVersion(&quot;1.0.0.0&quot;)]
--  [assembly: AssemblyFileVersion(&quot;1.0.0.0&quot;)]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;恢复-nuget-包&quot;&gt;恢复 NuGet 包&lt;/h2&gt;

&lt;p&gt;打开你原有项目的 packages.config 文件。这里记录了你的项目中已经安装的 NuGet 包。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;packages&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Toolkit.Wpf.UI.XamlHost&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net471&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/packages&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们需要把这个文件里面的内容转换成 PackageReference。按照如下的方式逐一将 &lt;code class=&quot;highlighter-rouge&quot;&gt;package&lt;/code&gt; 转换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Toolkit.Wpf.UI.XamlHost&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，csproj 项目文件的内容如下：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;UseWPF&amp;gt;true&amp;lt;/UseWPF&amp;gt;
        &amp;lt;OutputType&amp;gt;WinExe&amp;lt;/OutputType&amp;gt;
        &amp;lt;ApplicationManifest&amp;gt;Properties\App.manifest&amp;lt;/ApplicationManifest&amp;gt;
        &amp;lt;ApplicationIcon&amp;gt;Properties\App.ico&amp;lt;/ApplicationIcon&amp;gt;
        &amp;lt;StartupObject&amp;gt;Walterlv.Whitman.Program&amp;lt;/StartupObject&amp;gt;
      &amp;lt;/PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;ItemGroup&amp;gt;
++      &amp;lt;PackageReference Include=&quot;Microsoft.Toolkit.Wpf.UI.XamlHost&quot; Version=&quot;5.0.0&quot; /&amp;gt;
++    &amp;lt;/ItemGroup&amp;gt;
&lt;/span&gt;      &amp;lt;ItemGroup&amp;gt;
        &amp;lt;Resource Include=&quot;Properties\App.ico&quot; /&amp;gt;
      &amp;lt;/ItemGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你觉得这一步骤比较繁琐，那么可以在本文一开始就按照这篇博客的方式进行操作：&lt;a href=&quot;/post/migrate-packages-config-to-package-reference&quot;&gt;自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;添加-windows-兼容包&quot;&gt;添加 Windows 兼容包&lt;/h2&gt;

&lt;p&gt;如果你原有的 WPF 项目引用了一些注册表等 Windows 特有的功能，那么你还需要引用一个 Windows 兼容 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Windows.Compatibility&quot;&gt;Microsoft.Windows.Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;编译运行和修复其他错误&quot;&gt;编译、运行和修复其他错误&lt;/h2&gt;

&lt;p&gt;对于比较简单的项目，在经过以上步骤之后，你可能已经可以可以直接跑起来了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-06-10-00-06.png&quot; alt=&quot;运行&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于复杂一些的项目，你可能会遇到其他的编译或运行错误，你需要适当进行一些修复。而产生这些错误的原因是 csproj 文件中删除了太多的东西。你需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup /&amp;gt;&lt;/code&gt; 中的一些没有默认添加进来的文件加入进来。&lt;/p&gt;

&lt;h2 id=&quot;更多&quot;&gt;更多&lt;/h2&gt;

&lt;p&gt;如果你只是希望创建基于 .NET Core 3 的新 WPF 项目，那么请阅读我的另一篇博客：&lt;a href=&quot;/post/create-new-wpf-on-dotnet-core-project&quot;&gt;如何创建一个基于 .NET Core 3 的 WPF 项目&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可以持续关注官方 WPF on .NET Core 的例子：&lt;a href=&quot;https://github.com/dotnet/samples/tree/master/wpf/WPF-WinRT&quot;&gt;samples/wpf/WPF-WinRT at master · dotnet/samples&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 31 May 2019 12:11:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/migrate-wpf-project-from-dotnet-framework-to-dotnet-core.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/migrate-wpf-project-from-dotnet-framework-to-dotnet-core.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>.NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉</title>
        <description>&lt;p&gt;当我们不再使用某个对象的时候，此对象会被 GC 垃圾回收掉。当然前提是你没有写出内存泄漏的代码。我们也知道如果生成了大量的字符串，会对 GC 造成很大的压力。&lt;/p&gt;

&lt;p&gt;但是，如果在编译期间能够确定的字符串，就不会被 GC 垃圾回收掉了。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;示例代码&quot;&gt;示例代码&lt;/h2&gt;

&lt;p&gt;下面，我创建了几个字符串，我关心的字符串是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;walterlv&quot;&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;lindexi&quot;&lt;/code&gt; 以及一个当前时间。&lt;/p&gt;

&lt;p&gt;于是使用下面的代码来验证：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lindexi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;林德熙&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;时间&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;开始个数：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;剩余个数：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;walterlv&quot;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;lindexi&quot;&lt;/code&gt; 是在编译期间能够完全确定的字符串，而当前时间字符串我们都知道是编译期间不能确定的字符串。&lt;/p&gt;

&lt;p&gt;在 GC 收集之前和之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&lt;/code&gt; 中的对象数量从三个降到了两个。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-28-21-21-55.png&quot; alt=&quot;运行结果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;并没有清除成 0 个，说明字符串现在仍然是被引用着的。&lt;/p&gt;

&lt;p&gt;那被什么引用着呢？是字符串暂存池。要理解字符串暂存池，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/string-intern-pool&quot;&gt;.NET/C# 的字符串暂存池&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，即便设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompilationRelaxations.NoStringInterning&lt;/code&gt;，编译期间能确定的字符串在上述代码中也是不会被垃圾回收的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2423134/6233938&quot;&gt;c# - Strings and Garbage Collection - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 28 May 2019 13:31:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/compile-time-strings-are-in-the-string-intern-pool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/compile-time-strings-are-in-the-string-intern-pool.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 的字符串暂存池</title>
        <description>&lt;p&gt;本文介绍 .NET 中的字符串暂存池。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;字符串暂存池&quot;&gt;字符串暂存池&lt;/h2&gt;

&lt;p&gt;.NET 的 CLR 运行时会在运行期间管理一个字符串暂存池（string intern pool），在字符串暂存池中的字符串只有一个实例。&lt;/p&gt;

&lt;p&gt;例如，在下面的代码中，变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt; 都是同一个实例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我有另一篇博客说到了此问题，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/same-strings-at-compile-time-are-the-same-instances-at-runtime&quot;&gt;.NET/C# 编译期间能确定的相同字符串，在运行期间是相同的实例&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;字符串暂存池的出现是为了避免分配大量的字符串对象造成的过多的内存空间浪费。&lt;/p&gt;

&lt;h2 id=&quot;编译期间确定&quot;&gt;编译期间确定&lt;/h2&gt;

&lt;p&gt;默认进入字符串暂存池中的字符串是那些写程序的时候直接声明或者直接写入代码中的字符串。上一节中列举的三个变量中的字符串就是直接写到代码中的字符串。&lt;/p&gt;

&lt;p&gt;默认情况下编译期间能确定出来的字符串会写入到程序集中，运行时能直接将其放入字符串暂存池。&lt;/p&gt;

&lt;h2 id=&quot;从暂存池中获取字符串&quot;&gt;从暂存池中获取字符串&lt;/h2&gt;

&lt;p&gt;现在，我们要制造出编译期间不能确定出来的字符串，以便进行一些试验。&lt;/p&gt;

&lt;p&gt;我们当然不能使用简单的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;walter&quot; + &quot;lv&quot;&lt;/code&gt; 这样简单的字符串拼接的方式来生成字符串，因为实际上这样的字符串依然可以在编译期间完全确定。&lt;/p&gt;

&lt;p&gt;所以这里使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;StringBuilder&lt;/code&gt; 来在运行期间生成字符串。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Intern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，虽然 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt; 三个字符串的值都是相等的，但 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 两个字符串是不同的实例，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;c&lt;/code&gt; 两个字符串是相同的实例。&lt;/p&gt;

&lt;p&gt;我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Intern&lt;/code&gt; 方法从字符串池中取出了一个字符串的实例。&lt;/p&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt; 类型还提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.IsInterned&lt;/code&gt; 来判断一个字符串是否在字符串暂存池中。&lt;/p&gt;

&lt;h2 id=&quot;不要池化&quot;&gt;不要池化&lt;/h2&gt;

&lt;p&gt;你可以在程序集中标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompilationRelaxations.NoStringInterning&lt;/code&gt;，这样，此程序集中的字符串就不会被池化。即便是在编译期间写下的字符串也会在运行时生成新的实例。&lt;/p&gt;

&lt;p&gt;方法是在一个 C# 代码文件中添加特性标记。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoStringInterning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;垃圾回收&quot;&gt;垃圾回收&lt;/h2&gt;

&lt;p&gt;在字符串暂存池中的字符串不会被垃圾回收，你可以阅读另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/-compile-time-strings-are-in-the-string-intern-pool&quot;&gt;.NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.string.intern?redirectedfrom=MSDN&amp;amp;view=netframework-4.8#System_String_Intern_System_String_&quot;&gt;String.Intern(String) Method (System) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 28 May 2019 13:26:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/string-intern-pool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/string-intern-pool.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 避免调试器不小心提前计算本应延迟计算的值</title>
        <description>&lt;p&gt;延迟计算属性的值，应该很多小伙伴都经常使用。比如在属性的 &lt;code class=&quot;highlighter-rouge&quot;&gt;get&lt;/code&gt; 方法中判断是否已初始化，如果没有初始化则立即开始初始化。&lt;/p&gt;

&lt;p&gt;但这样的写法存在一个很大的问题——如果你使用 Visual Studio 调试，当你把鼠标划到对象的实例上的时候，属性就会立刻开始进行初始化。而此时对你的代码来说可能就过早初始化了。我们不应该让调试器非预期地影响到我们程序的执行结果。&lt;/p&gt;

&lt;p&gt;本文介绍如何避免调试器不小心提前计算本应延迟计算的值。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;方法是在属性上添加一个特性 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerBrowsableAttribute&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当指定为不再显示的话，在调试器中查看此实例的属性的时候就看不到这个属性了，也就不会因为鼠标划过导致提前计算了值。&lt;/p&gt;

&lt;p&gt;当然，如果你希望为你的类型定制更多的调试器显示方式，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/display-instance-info-in-custom-debugger-view&quot;&gt;C#/.NET 调试的时候显示自定义的调试信息（DebuggerDisplay 和 DebuggerTypeProxy） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/Lazy.cs,5379c104fa6e2022&quot;&gt;Lazy.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 28 May 2019 10:51:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/avoid-value-been-evaluated-by-debugger.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/avoid-value-been-evaluated-by-debugger.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>cmd.exe 的命令行启动参数（可用于执行命令、传参或进行环境配置）</title>
        <description>&lt;p&gt;有一些程序不支持被直接启动，而要求通过命令行启动。这个时候，你就需要使用 cmd.exe 来启动这样的程序。我们都知道如何在 cmd.exe 中启动一个程序，但是当你需要自动启动这个程序的时候，你就需要知道如何通过 cmd.exe 来启动一个程序，而不是手工输入然后回车运行了。&lt;/p&gt;

&lt;p&gt;本文就介绍 cmd.exe 的命令行启动参数。利用这些参数，你可以自动化地通过 cmd.exe 程序来完成一些原本需要通过手工执行的操作或者突破一些限制。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一些必须通过命令行启动的程序&quot;&gt;一些必须通过命令行启动的程序&lt;/h2&gt;

&lt;p&gt;一般来说，编译生成的 exe 程序都可以直接启动，即便是命令行程序也是如此。但是有一些程序就是要做一些限制。比如下面的 FRP 反向代理程序：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-12-12-48-57.png&quot; alt=&quot;FRP 反向代理程序限制必须从命令行启动&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么我们如何能够借助于 cmd.exe 来启动它呢？接下来说明。&lt;/p&gt;

&lt;p&gt;顺便，使用 PowerShell 来启动的方法可以参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/powershell-startup-arguments&quot;&gt;PowerShell 的命令行启动参数（可用于执行命令、传参或进行环境配置） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cmdexe-的帮助文档&quot;&gt;cmd.exe 的帮助文档&lt;/h2&gt;

&lt;p&gt;先打开一个 cmd，然后输入：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你就可以看到 cmd.exe 的使用说明：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-12-12-54-33.png&quot; alt=&quot;cmd.exe 的使用说明&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;启动 Windows 命令解释器的一个新实例&lt;/p&gt;

  &lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/E:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/E:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/V:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/V:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/C&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;你可以随时输入上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd /?&lt;/code&gt; 命令来查看这些参数详细说明，所以本文不会非常详细地列举各个参数的含义，只会列出一些常见的使用示例。&lt;/p&gt;

&lt;h2 id=&quot;cmdexe-的启动参数示例&quot;&gt;cmd.exe 的启动参数示例&lt;/h2&gt;

&lt;h3 id=&quot;使用-cmdexe-间接启动一个程序并传入参数&quot;&gt;使用 cmd.exe 间接启动一个程序并传入参数&lt;/h3&gt;

&lt;p&gt;下面的命令，使用 cmd 间接启动 frpc.exe 反向代理程序，并给 frpc.exe 程序传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;-c ./frpc.ini&lt;/code&gt; 的启动参数：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\walterlv\frp\frpc.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/frpc.ini&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于为什么会用这种方式启动 frpc.exe，则是为了设置 frpc.exe 为开机自动启动。&lt;/p&gt;

&lt;p&gt;因为我写了一些 Asp.NET Core 的服务，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-通过-frp-发布自己的网站.html&quot;&gt;dotnet core 通过 frp 发布自己的网站 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，间接启动一个程序的时候也可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;/k&lt;/code&gt; 参数。与 &lt;code class=&quot;highlighter-rouge&quot;&gt;/c&lt;/code&gt; 参数不同的是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/c&lt;/code&gt; 在执行完程序之后，cmd.exe 也会终止&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/k&lt;/code&gt; 在执行完程序之后，cmd.exe 依然会继续运行&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;/c&lt;/code&gt; 命令会更适用于自动化的脚本，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;/k&lt;/code&gt; 命令则更适用于半自动化的脚本。&lt;/p&gt;

&lt;h2 id=&quot;cmdexe-启动参数使用中的坑&quot;&gt;cmd.exe 启动参数使用中的坑&lt;/h2&gt;

&lt;p&gt;在上面的例子中，我们的路径中不涉及到空格。我们知道，路径中有空格的话，在命令行中使用需要加上引号。但实际上如果你真的给路径加上了引号，会发现 cmd.exe 就开始不识别你的命令路径了。&lt;/p&gt;

&lt;p&gt;这个时候，你需要在整个传给 cmd.exe 的命令外层再加一层引号：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\walterlv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;folders\frp\frpc.exe&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; -c ./frpc.ini &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上，感谢 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 挥泪踩出来的坑，详见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/%E5%A6%82%E4%BD%95%E5%9C%A8-cmd-%E5%90%AF%E5%8A%A8%E7%9A%84%E8%BD%AF%E4%BB%B6%E4%BC%A0%E5%85%A5%E5%B8%A6%E7%A9%BA%E6%A0%BC%E7%9A%84%E8%B7%AF%E5%BE%84&quot;&gt;如何在 CMD 启动的软件传入带空格的路径 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;附-cmdexe-的全部启动参数说明&quot;&gt;附 cmd.exe 的全部启动参数说明&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;启动 Windows 命令解释器的一个新实例&lt;/p&gt;

  &lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/E:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/E:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/F:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/V:ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/V:OFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/C&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;/C      执行字符串指定的命令然后终止
/K      执行字符串指定的命令但保留
/S      修改 /C 或 /K 之后的字符串处理(见下)
/Q      关闭回显
/D      禁止从注册表执行 AutoRun 命令(见下)
/A      使向管道或文件的内部命令输出成为 ANSI
/U      使向管道或文件的内部命令输出成为
        Unicode
/T:fg   设置前台/背景颜色(详细信息见 COLOR /?)
/E:ON   启用命令扩展(见下)
/E:OFF  禁用命令扩展(见下)
/F:ON   启用文件和目录名完成字符(见下)
/F:OFF  禁用文件和目录名完成字符(见下)
/V:ON   使用 ! 作为分隔符启用延迟的环境变量
        扩展。例如，/V:ON 会允许 !var! 在执行时
        扩展变量 var。var 语法会在输入时
        扩展变量，这与在一个 FOR
        循环内不同。
/V:OFF  禁用延迟的环境扩展。&lt;/p&gt;

  &lt;p&gt;注意，如果字符串加有引号，可以接受用命令分隔符 “&amp;amp;&amp;amp;”
分隔多个命令。另外，由于兼容性
原因，/X 与 /E:ON 相同，/Y 与 /E:OFF 相同，且 /R 与
/C 相同。任何其他开关都将被忽略。&lt;/p&gt;

  &lt;p&gt;如果指定了 /C 或 /K，则会将该开关之后的
命令行的剩余部分作为一个命令行处理，其中，会使用下列逻辑
处理引号(“)字符:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1.  如果符合下列所有条件，则会保留
    命令行上的引号字符:

    - 不带 /S 开关
    - 正好两个引号字符
    - 在两个引号字符之间无任何特殊字符，
      特殊字符指下列字符: &amp;amp;&amp;lt;&amp;gt;()@^|
    - 在两个引号字符之间至少有
      一个空格字符
    - 在两个引号字符之间的字符串是某个
      可执行文件的名称。

2.  否则，老办法是看第一个字符
    是否是引号字符，如果是，则去掉首字符并
    删除命令行上最后一个引号，保留
    最后一个引号之后的所有文本。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;如果 /D 未在命令行上被指定，当 CMD.EXE 开始时，它会寻找
以下 REG_SZ/REG_EXPAND_SZ 注册表变量。如果其中一个或
两个都存在，这两个变量会先被执行。&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun

    和/或

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;命令扩展是按默认值启用的。你也可以使用 /E:OFF ，为某一
特定调用而停用扩展。你
可以在机器上和/或用户登录会话上
启用或停用 CMD.EXE 所有调用的扩展，这要通过设置使用
REGEDIT.EXE 的注册表中的一个或两个 REG_DWORD 值:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\EnableExtensions

    和/或

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;到 0x1 或 0x0。用户特定设置
比机器设置有优先权。命令行
开关比注册表设置有优先权。&lt;/p&gt;

  &lt;p&gt;在批处理文件中，SETLOCAL ENABLEEXTENSIONS 或 DISABLEEXTENSIONS 参数
比 /E:ON 或 /E:OFF 开关有优先权。请参阅 SETLOCAL /? 获取详细信息。&lt;/p&gt;

  &lt;p&gt;命令扩展包括对下列命令所做的
更改和/或添加:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DEL or ERASE
COLOR
CD or CHDIR
MD or MKDIR
PROMPT
PUSHD
POPD
SET
SETLOCAL
ENDLOCAL
IF
FOR
CALL
SHIFT
GOTO
START (同时包括对外部命令调用所做的更改)
ASSOC
FTYPE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;有关特定详细信息，请键入 commandname /? 查看。&lt;/p&gt;

  &lt;p&gt;延迟环境变量扩展不按默认值启用。你
可以用/V:ON 或 /V:OFF 开关，为 CMD.EXE 的某个调用而
启用或停用延迟环境变量扩展。你
可以在机器上和/或用户登录会话上启用或停用 CMD.EXE 所有
调用的延迟扩展，这要通过设置使用 REGEDIT.EXE 的注册表中的
一个或两个 REG_DWORD 值:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion

    和/或

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;到 0x1 或 0x0。用户特定设置
比机器设置有优先权。命令行开关
比注册表设置有优先权。&lt;/p&gt;

  &lt;p&gt;在批处理文件中，SETLOCAL ENABLEDELAYEDEXPANSION 或 DISABLEDELAYEDEXPANSION
参数比 /V:ON 或 /V:OFF 开关有优先权。请参阅 SETLOCAL /?
获取详细信息。&lt;/p&gt;

  &lt;p&gt;如果延迟环境变量扩展被启用，
惊叹号字符可在执行时间被用来
代替一个环境变量的数值。&lt;/p&gt;

  &lt;p&gt;你可以用 /F:ON 或 /F:OFF 开关为 CMD.EXE 的某个
调用而启用或禁用文件名完成。你可以在计算上和/或
用户登录会话上启用或禁用 CMD.EXE 所有调用的完成，
这可以通过使用 REGEDIT.EXE 设置注册表中的下列
 REG_DWORD 的全部或其中之一:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\CompletionChar
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\PathCompletionChar

    和/或

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\PathCompletionChar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;由一个控制字符的十六进制值作为一个特定参数(例如，0x4
是Ctrl-D，0x6 是 Ctrl-F)。用户特定设置优先于机器设置。
命令行开关优先于注册表设置。&lt;/p&gt;

  &lt;p&gt;如果完成是用 /F:ON 开关启用的，两个要使用的控制符是:
目录名完成用 Ctrl-D，文件名完成用 Ctrl-F。要停用
注册表中的某个字符，请用空格(0x20)的数值，因为此字符
不是控制字符。&lt;/p&gt;

  &lt;p&gt;如果键入两个控制字符中的一个，完成会被调用。完成功能将
路径字符串带到光标的左边，如果没有通配符，将通配符附加
到左边，并建立相符的路径列表。然后，显示第一个相符的路
径。如果没有相符的路径，则发出嘟嘟声，不影响显示。之后，
重复按同一个控制字符会循环显示相符路径的列表。将 Shift
键跟控制字符同时按下，会倒着显示列表。如果对该行进行了
任何编辑，并再次按下控制字符，保存的相符路径的列表会被
丢弃，新的会被生成。如果在文件和目录名完成之间切换，会
发生同样现象。两个控制字符之间的唯一区别是文件完成字符
符合文件和目录名，而目录完成字符只符合目录名。如果文件
完成被用于内置式目录命令(CD、MD 或 RD)，就会使用目录
完成。
用引号将相符路径括起来，完成代码可以正确处理含有空格
或其他特殊字符的文件名。同时，如果备份，然后从行内调用
文件完成，完成被调用时位于光标右方的文字会被调用。&lt;/p&gt;

  &lt;p&gt;需要引号的特殊字符是:
     &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;space&amp;gt;&lt;/code&gt;
     &lt;code class=&quot;highlighter-rouge&quot;&gt;()[]{}^=;!'+,&lt;/code&gt;~(&amp;amp;()`&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Sat, 25 May 2019 01:50:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/cmd-startup-arguments.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/cmd-startup-arguments.html</guid>
        
        
        <category>windows</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 中重新将高级保存功能放出来，便于强制指定文件编码格式</title>
        <description>&lt;p&gt;Visual Studio 的早期版本中有一个高级保存功能，但是升级到 Visual Studio 2019 之后这个功能就不在菜单项里面了。&lt;/p&gt;

&lt;p&gt;本文将带你把它找出来继续使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步工具---自定义&quot;&gt;第一步：工具 -&amp;gt; 自定义&lt;/h2&gt;

&lt;p&gt;打开 Visual Studio 2019，然后进入“工具 -&amp;gt; 自定义”菜单项。对于英文版本，是“Tools -&amp;gt; Customize”菜单项。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-20-17-12.png&quot; alt=&quot;工具 -&amp;gt; 自定义&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步自定义命令&quot;&gt;第二步：自定义命令&lt;/h2&gt;

&lt;p&gt;按照下图一个个点击，把“高级保存选项”放出来：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-20-21-03.png&quot; alt=&quot;放出高级保存选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当刚刚添加出来的时候，位置可能不太正确，但是我们可以点击窗口旁边的“上移”和“下移”按钮将其放在合适的位置。&lt;/p&gt;

&lt;p&gt;为了照顾英文版，我也放出英文版的界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-20-25-22.png&quot; alt=&quot;English Save Options&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 23 May 2019 12:25:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/make-advanced-save-out-in-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/make-advanced-save-out-in-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>WPF 使用 AppBar 将窗口停靠在桌面上，让其他程序不占用此窗口的空间（附我封装的附加属性）</title>
        <description>&lt;p&gt;本文介绍如何使用 Windows 的 AppBar 相关 API 实现固定停靠在桌面上的特殊窗口。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;停靠窗口&quot;&gt;停靠窗口&lt;/h2&gt;

&lt;p&gt;你可能并不明白停靠窗口是什么意思。&lt;/p&gt;

&lt;p&gt;看下图，你可能使用过 OneNote 的停靠窗口功能。当打开一个新的 OneNote 停靠窗口之后，这个新的 OneNote 窗口将固定显示在桌面的右侧，其他的窗口就算最大化也只会占据剩余的空间。&lt;/p&gt;

&lt;p&gt;OneNote 的这种功能可以让你在一边浏览网页或做其他事情的时候，以便能够做笔记。同时又不用担心其他窗口最大化的时候会占据记笔记的一部分空间。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-22-16-57-28.png&quot; alt=&quot;OneNote 的停靠窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这其实也是 Windows 任务栏所使用的方法。&lt;/p&gt;

&lt;p&gt;OneNote 中给出的名称叫做“停靠窗口”，于是这可以代表微软希望用户对这个概念的理解名词。&lt;/p&gt;

&lt;p&gt;只是，这个概念在 Windows API 中的名称叫做 AppBar。&lt;/p&gt;

&lt;h2 id=&quot;appbar&quot;&gt;AppBar&lt;/h2&gt;

&lt;p&gt;要做出停靠窗口的效果，最核心的 API 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SHAppBarMessage&lt;/code&gt;，用于发送 AppBar 消息给操作系统，以便让操作系统开始处理此窗口已形成一个 AppBar 窗口。也就是我们在用户交互上所说的“停靠窗口”。&lt;/p&gt;

&lt;p&gt;虽然说要让一个窗口变成 AppBar 只需要一点点代码，但是要让整个停靠窗口工作得真的像一个停靠窗口，依然需要大量的辅助代码。所以我将其封装成了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesktopAppBar&lt;/code&gt; 类，方便 WPF 程序来调用。&lt;/p&gt;

&lt;h2 id=&quot;如何使用&quot;&gt;如何使用&lt;/h2&gt;

&lt;p&gt;以下使用，你需要先获取我封装的源码才可以编译通过：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/1169952f73f44a8623bbbf7e1ca1a342&quot;&gt;https://gist.github.com/walterlv/1169952f73f44a8623bbbf7e1ca1a342&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你可以在 XAML 中使用：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.DesktopDocking.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:dock=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.DesktopDocking&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv 的停靠窗口&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;500&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dock:DesktopAppBar.AppBar=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#ffcd42&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv 的停靠窗口&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;再停靠一个 - blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Padding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#f9d77b&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button_Click&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;核心代码是其中的一处属性赋值 &lt;code class=&quot;highlighter-rouge&quot;&gt;dock:DesktopAppBar.AppBar=&quot;Right&quot;&lt;/code&gt;，以及前面的命名空间声明 &lt;code class=&quot;highlighter-rouge&quot;&gt;xmlns:dock=&quot;clr-namespace:Walterlv.Demo.DesktopDocking&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你也可以在 C# 代码中使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DesktopDocking&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DesktopAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用以上代码中的任何一种方式，你就可以让你的窗口在右边停靠了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-22-19-29-33.png&quot; alt=&quot;停靠的窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中我们可以发现，我们的示例窗口停靠在了右边，其宽度就是我们在 XAML 中设置的窗口宽度（当然这是我封装的逻辑，而不是 AppBar 的原生逻辑）。&lt;/p&gt;

&lt;p&gt;同时我们还能注意到，Visual Studio 的窗口是处于最大化的状态的——这是停靠窗口的最大优势——可以让其他窗口的工作区缩小，在最大化的时候不会覆盖到停靠窗口的内容。&lt;/p&gt;

&lt;p&gt;另外，如果设置了第二个停靠窗口，那么第二个停靠窗口会挤下第一个窗口的位置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-22-20-02-29.png&quot; alt=&quot;两个停靠窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;如何还原&quot;&gt;如何还原&lt;/h2&gt;

&lt;p&gt;Windows AppBar 的 API 有一个很不好的设定，如果进程退出了，那么 AppBar 所占用的空间 &lt;strong&gt;并不会还原&lt;/strong&gt;！！！&lt;/p&gt;

&lt;p&gt;不过不用担心，我在封装的代码里面加入了窗口关闭时还原空间的代码，如果你正常关闭窗口，那么停靠窗口占用的空间就会及时还原回来。&lt;/p&gt;

&lt;p&gt;当然，你也可以适时调用下面的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;DesktopAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;附源码&quot;&gt;附源码&lt;/h2&gt;

&lt;p&gt;由于源码一直在持续改进，所以本文中贴的源代码可能不是最新的。你可以在以下仓库找到这段源码的最新版本：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.demo/tree/master/Walterlv.Demo.DesktopDocking/Walterlv.Demo.DesktopDocking&quot;&gt;walterlv.demo/Walterlv.Demo.DesktopDocking/Walterlv.Demo.DesktopDocking at master · walterlv/walterlv.demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ReSharper disable IdentifierTypo&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ReSharper disable InconsistentNaming&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ReSharper disable EnumUnderlyingTypeIsInt&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ReSharper disable MemberCanBePrivate.Local&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ReSharper disable UnusedMember.Local&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ReSharper disable UnusedMember.Global&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.DesktopDocking&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 表示窗口停靠到桌面上时的边缘方向。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 窗口停靠到桌面的左边。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 窗口停靠到桌面的顶部。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 窗口停靠到桌面的右边。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 窗口停靠到桌面的底部。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 窗口不停靠到任何方向，而是成为一个普通窗口占用剩余的可用空间（工作区）。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供将窗口停靠到桌面某个方向的能力。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DesktopAppBar&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 标识 Window.AppBar 的附加属性。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;AppBar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesktopAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnAppBarEdgeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取 &amp;lt;paramref name=&quot;window&quot;/&amp;gt; 当前的停靠边缘。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;window&quot;&amp;gt;要获取停靠边缘的窗口。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;停靠边缘。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 设置 &amp;lt;paramref name=&quot;window&quot;/&amp;gt; 的停靠边缘方向。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;window&quot;&amp;gt;要设置停靠的窗口。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;value&quot;&amp;gt;要设置的停靠边缘方向。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarProcessorProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;AppBarProcessor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarWindowProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesktopAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SuppressMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReSharper&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ConditionIsAlwaysTrueOrFalse&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAppBarEdgeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesignerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetIsInDesignMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarWindowProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProcessorProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Detach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AppBarWindowProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProcessorProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarWindowProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProcessorProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 包含对 &amp;lt;see cref=&quot;Window&quot;/&amp;gt; 进行操作以便使其成为一个桌面停靠窗口的能力。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppBarWindowProcessor&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 创建 &amp;lt;see cref=&quot;AppBarWindowProcessor&quot;/&amp;gt; 的新实例。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;window&quot;&amp;gt;需要成为停靠窗口的 &amp;lt;see cref=&quot;Window&quot;/&amp;gt; 的实例。&amp;lt;/param&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AppBarWindowProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_callbackId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterWindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AppBarMessage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskCompletionSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Closed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnClosed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskCompletionSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_callbackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreResizeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreTopmost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 在可以获取到窗口句柄的时候，给窗口句柄设置值。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 在窗口关闭之后，需要恢复窗口设置过的停靠属性。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnClosed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Closed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnClosed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClearValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 将窗口属性设置为停靠所需的属性。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ForceWindowProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoResize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Topmost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 备份窗口在成为停靠窗口之前的属性。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BackupWindowProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_restoreStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_restoreBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RestoreBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_restoreResizeMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_restoreTopmost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Topmost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 使一个窗口开始成为桌面停靠窗口，并开始处理窗口停靠消息。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;value&quot;&amp;gt;停靠方向。&amp;lt;/param&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;BackupWindowProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;APPBARDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uCallbackMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_callbackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SHAppBarMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABMsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABM_NEW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 更新一个窗口的停靠方向。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;value&quot;&amp;gt;停靠方向。&amp;lt;/param&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;


                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformToAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RestoreBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;ForceWindowProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;Resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// 使一个窗口从桌面停靠窗口恢复成普通窗口。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Detach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;APPBARDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;SHAppBarMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABMsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABM_REMOVE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreResizeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Topmost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreTopmost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;Resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_restoreBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_callbackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABNotify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABN_POSCHANGED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hwndSourceTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformToAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RestoreBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;Resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformToAppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;APPBARDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrimaryScreenHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrimaryScreenWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrimaryScreenWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBarEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrimaryScreenHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;SHAppBarMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABMsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABM_QUERYPOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SHAppBarMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABMsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABM_SETPOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RECT&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StructLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LayoutKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;APPBARDATA&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cbSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uCallbackMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABMsg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_NEW&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_REMOVE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_QUERYPOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_SETPOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_GETSTATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_GETTASKBARPOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_ACTIVATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_GETAUTOHIDEBAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_SETAUTOHIDEBAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_WINDOWPOSCHANGED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABM_SETSTATE&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ABNotify&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABN_STATECHANGE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABN_POSCHANGED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABN_FULLSCREENAPP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ABN_WINDOWARRANGE&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHELL32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CallingConvention&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CallingConvention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StdCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SHAppBarMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;APPBARDATA&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterWindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/84987/6233938&quot;&gt;c# - How do you do AppBar docking (to screen edge, like WinAmp) in WPF? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mgaffigan/WpfAppBar&quot;&gt;mgaffigan/WpfAppBar: AppBar implementation for WPF&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2792047/6233938&quot;&gt;.net - How to dock an application in the Windows desktop? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/6741/AppBar-using-C&quot;&gt;AppBar using C# - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shappbarmessage&quot;&gt;SHAppBarMessage function (shellapi.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registerwindowmessagea&quot;&gt;RegisterWindowMessageA function (winuser.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 23 May 2019 10:53:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dock-window-into-windows-desktop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dock-window-into-windows-desktop.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>在整个 Git 仓库的历史（包括所有分支和标签）中修改提交作者的信息（姓名和邮箱）</title>
        <description>&lt;p&gt;一般情况下不建议修改 git 仓库的历史。&lt;/p&gt;

&lt;p&gt;但是现在我计划开源我的一个项目，于是自己个人使用的姓名和邮箱就需要在开源的时候改为使用我公开的姓名和邮箱。对于旧仓库，我将废弃，将来所有的精力都将在开源版本的仓库中；而对于开源版本的新仓库，由于此前没有人克隆过，所以也不会因为历史的修改产生问题。所以，我可以很放心地更改全部的 git 仓库历史。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我打算将整个 Git 仓库历史中的名称和邮箱。&lt;/p&gt;

&lt;h2 id=&quot;第一步打开-git-bash&quot;&gt;第一步：打开 Git Bash&lt;/h2&gt;

&lt;p&gt;进入本地的 Git 仓库目录，然后打开 Git Bash。&lt;/p&gt;

&lt;h2 id=&quot;第二步输入-git-命令&quot;&gt;第二步：输入 Git 命令&lt;/h2&gt;

&lt;p&gt;接下来，我们需要输入一段多行命令。请先复制以下命令到你的临时编辑器中，然后修改这段多行命令中的几个变量的值。&lt;/p&gt;

&lt;p&gt;多行命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--env-filter&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'

OLD_EMAIL=&quot;your-old-email@example.com&quot;
CORRECT_NAME=&quot;Your Correct Name&quot;
CORRECT_EMAIL=&quot;your-correct-email@example.com&quot;

if [ &quot;$GIT_COMMITTER_EMAIL&quot; = &quot;$OLD_EMAIL&quot; ]
then
    export GIT_COMMITTER_NAME=&quot;$CORRECT_NAME&quot;
    export GIT_COMMITTER_EMAIL=&quot;$CORRECT_EMAIL&quot;
fi
if [ &quot;$GIT_AUTHOR_EMAIL&quot; = &quot;$OLD_EMAIL&quot; ]
then
    export GIT_AUTHOR_NAME=&quot;$CORRECT_NAME&quot;
    export GIT_AUTHOR_EMAIL=&quot;$CORRECT_EMAIL&quot;
fi
'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tag-name-filter&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--branches&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请注意上面那几个变量：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OLD_EMAIL&lt;/code&gt; 修改为你的旧邮箱（也就是需要替换掉的 Git 历史中的邮箱）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CORRECT_NAME&lt;/code&gt; 修改为你的新名称&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CORRECT_EMAIL&lt;/code&gt; 修改为你的新邮箱&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对我来说，新名称也就是我在 GitHub 上的名称 &lt;a href=&quot;https://github.com/walterlv&quot;&gt;walterlv&lt;/a&gt;，新邮箱也就是我在 GitHub 上公开使用的提交邮箱。&lt;/p&gt;

&lt;p&gt;将以上修改后的命令粘贴到 Git Bash 中，然后按下回车键执行命令：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-16-07-59.png&quot; alt=&quot;执行命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等待命令执行结束，你就能看到你的仓库中所有的分支（Branches）、所有的标签（Tags）中的旧作者信息全部被替换为了新作者信息了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-16-06-39.png&quot; alt=&quot;执行结果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步推送仓库&quot;&gt;第三步：推送仓库&lt;/h2&gt;

&lt;p&gt;如果你只是准备开源这个仓库，还没开始推送，那么直接推送即可。使用以下命令推送所有的分支和所有的标签。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt; origin &lt;span class=&quot;s1&quot;&gt;'refs/heads/*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你已经将仓库推送出去了，那么就需要强制推送来覆盖远端的仓库。使用以下命令推送所有的分支和所有的标签。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt; origin &lt;span class=&quot;s1&quot;&gt;'refs/heads/*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://help.github.com/en/articles/changing-author-info&quot;&gt;Changing author info - GitHub Help&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 23 May 2019 08:15:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/modify-author-info-of-the-whole-git-history.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/modify-author-info-of-the-whole-git-history.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用 ConditionalWeakTable 附加字段（CLR 版本的附加属性，也可用用来当作弱引用字典 WeakDictionary）</title>
        <description>&lt;p&gt;如果你使用过 WPF/UWP 等 XAML UI 框架，那么应该了解到附加属性的概念。那么没有依赖属性支持的时候如何做附加属性的功能呢？你可能会想到弱引用。但这需要做一个弱引用字典，要写的代码还是非常麻烦的。&lt;/p&gt;

&lt;p&gt;本文介绍 .NET 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 类型，适用于 .NET Framework 4.0 以上和全部 .NET Core 的版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;这不是字典&quot;&gt;这不是字典&lt;/h2&gt;

&lt;p&gt;现成可用的弱引用字典，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt;。然而实际上这个类的原本作用并不是当作字典使用！&lt;/p&gt;

&lt;p&gt;如果你使用过 WPF/UWP 等 XAML UI 框架，那么应该了解到附加属性的概念。这其实是 .NET 为我们提供的一种附加字段的机制。&lt;/p&gt;

&lt;p&gt;比如你有一个类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 请忽略这里公有字段带来的设计问题，只是为了演示。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们希望为它增加一个字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么我们需要修改类 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 本身以实现这个效果；但是这样就使得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 耦合了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt;，从而破坏了内聚性/依赖倒置原则。典型的情况是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类表示一个人 &lt;code class=&quot;highlighter-rouge&quot;&gt;Person&lt;/code&gt;，它里面不应该包含一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;某行账号&lt;/code&gt; 这样的字段，因为很多人是没有那家银行账号的。这个信息让那家银行存起来才是比较符合设计原则的设计。&lt;/p&gt;

&lt;p&gt;我们可以通过一个字典 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dictionary&amp;lt;Foo, Bar&amp;gt;&lt;/code&gt; 来存储所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 实例额外增加的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 的值可以避免让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类中增加 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 字段从而获得更好的设计。但这样就引入了一个静态字典从而使得所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 的实例无法得到释放。我们想当然希望拥有一个弱引用字典来解决问题。然而这是一个 &lt;a href=&quot;https://coolshell.cn/articles/10804.html&quot;&gt;X-Y 问题&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;实际上 .NET 中提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 帮我们解决了最本质的问题——在部分场景下期望为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类添加一个字段。虽然它不是弱引用字典，但能解决此类问题，同时也能当作一个弱引用字典来使用，仅此而已。&lt;/p&gt;

&lt;p&gt;你需要注意的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 并不实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 接口，只是里面有一些像 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; 的方法，可以当作字典使用，也可以遍历取出剩下的所有值。&lt;/p&gt;

&lt;h2 id=&quot;验证&quot;&gt;验证&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 中的所有 Key 和所有的 Value 都是弱引用的，并且会在其 Key 被回收或者 Key 和 Value 都被回收之后自动从集合中消失。这意味着当你使用它来为一个类型附加一些字段或者属性的时候完全不用担心内存泄漏的问题。&lt;/p&gt;

&lt;p&gt;下面我写了一段代码用于验证其内存泄漏问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;向 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 中添加了三个键值对；&lt;/li&gt;
  &lt;li&gt;将后两个的 &lt;code class=&quot;highlighter-rouge&quot;&gt;key&lt;/code&gt; 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;进行垃圾回收。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Weak&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Key1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Key2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Key3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weak2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$@&quot;key1 = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key2 = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, weak2 = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weak2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key3 = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Table = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{{{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}}}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvValue&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToShortTimeString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的运行结果如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-23-13-06-36.png&quot; alt=&quot;运行结果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从中我们可以发现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;当某个 Key 被回收后，&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 中就没有那一项键值对了；&lt;/li&gt;
  &lt;li&gt;当 Key 的实例依然在的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; 中的 Value 依然还会存在。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;另外，我们这里在调查内存泄漏问题，你需要在 Release 配置下执行此代码才能得到最符合预期的结果。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2&quot;&gt;ConditionalWeakTable&amp;lt;TKey,TValue&amp;gt; Class (System.Runtime.CompilerServices) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/12929019/6233938&quot;&gt;Good implementation of weak dictionary in .Net - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/nicholg/2006/06/04/presenting-weakdictionarytkey-tvalue/&quot;&gt;Presenting WeakDictionary[TKey, TValue] – Nick Guerrera’s blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/18613811/6233938&quot;&gt;.net - Understanding ConditionalWeakTable - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 23 May 2019 05:17:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/conditional-weak-table.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/conditional-weak-table.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 判断一个对象是否是设计时的窗口类型，而不是运行时的窗口</title>
        <description>&lt;p&gt;当我们对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类型写一个附加属性的时候，在属性变更通知中我们需要判断依赖对象是否是一个窗口。但是，如果直接判断是否是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类型，那么在设计器中这个属性的设置就会直接出现异常。&lt;/p&gt;

&lt;p&gt;那么有没有什么方法能够得知这是一个设计时的窗口呢？这样就不会抛出异常，而能够完美支持设计器了。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;方法一判断设计时属性&quot;&gt;方法一：判断设计时属性&lt;/h2&gt;

&lt;p&gt;WPF 原生自带一个附加属性可以判断一个依赖对象是否来源于设计器。而这个属性就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesignerProperties.IsInDesignMode&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在 WPF 的设计器中，这个属性会被设计器重写元数据，指定其值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，而其他默认的情况下，它的默认值都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以通过判断这个值可以得知此时是否是在设计器中使用此附加属性。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesignerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetIsInDesignMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 通常我们考虑在设计器中不做额外的任何事情是最偷懒不会出问题的代码了。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我在这些博客中使用过这样的判断方法，可以参见源码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-markup-extension-in-control-template&quot;&gt;如何编写 WPF 的标记扩展 MarkupExtension，即便在 ControlTemplate/DataTemplate 中也能生效&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;方法二判断设计时窗口&quot;&gt;方法二：判断设计时窗口&lt;/h2&gt;

&lt;p&gt;上面的方法是个通用的判断设计器中的方法。不过，如果我们希望得到更多的设计器支持，而不是像上面那样直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; 导致此属性在设计器中一点效果都没有的话，我们需要进行更精确的判断。&lt;/p&gt;

&lt;p&gt;然而设计器中的类型我们不能直接引用到，所以可以考虑进行类型名称判断的方式。类型名称判断的方式会与 Visual Studio 的版本相关，所以实际上代码并不怎么好看。&lt;/p&gt;

&lt;p&gt;我将判断方法整理如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDesignTime&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 判断一个依赖对象是否是设计时的 &amp;lt;see cref=&quot;Window&quot;/&amp;gt;。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;window&quot;&amp;gt;要被判断设计时的 &amp;lt;see cref=&quot;Window&quot;/&amp;gt; 对象。&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果对象是设计时的 &amp;lt;see cref=&quot;Window&quot;/&amp;gt;，则返回 true，否则返回 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsDesignTimeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vs201920172015Window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vs2013Window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Expression.WpfPlatform.InstanceBuilders.WindowInstance&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesignerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetIsInDesignMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vs201920172015Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vs2013Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，只需要调用一下这个方法即可得到此窗口实例是否是设计时的窗口：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvDesignTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDesignTimeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 检测到如果是设计时的窗口，就跳过一些句柄等等一些真的需要一个窗口的代码调用。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 检测到真的是窗口，做一些真实窗口初始化需要做的事情。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这不是一个窗口，需要抛出异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 22 May 2019 11:53:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/check-dependency-object-is-a-design-time-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/check-dependency-object-is-a-design-time-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>通过修改环境变量修改当前进程使用的系统 Temp 文件夹的路径</title>
        <description>&lt;p&gt;Windows 系统提供了一个在 Windows 单个用户下全局的 Temp 文件夹，用于给各种不同的应用程序提供一个临时目录。但是，直到 Windows 10 推出存储感知功能之前，这个文件夹都一直只归各个应用程序自己管理，应用自己需要删除里面的文件。另外，进程多了，临时文件也会互相影响（例如个数过多、进程读写竞争等等）。&lt;/p&gt;

&lt;p&gt;本文介绍将自己当前进程的 Temp 文件夹临时修改到应用程序自己的一个临时目录下，避免与其他程序之间的各种影响，同时也比较容易自行清理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何修改-temp-文件夹的路径&quot;&gt;如何修改 Temp 文件夹的路径&lt;/h2&gt;

&lt;p&gt;在程序启动的时候，调用如下方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTempFolder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Walterlv\ApplicationTemp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TEMP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTempFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TMP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTempFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，可以将当前进程的临时文件夹设置到 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Walterlv\ApplicationTemp&lt;/code&gt; 文件夹下。&lt;/p&gt;

&lt;p&gt;上面设置了两个环境变量，实际上 .NET Framework 中主要使用的临时文件夹环境变量是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TMP&lt;/code&gt; 那个。&lt;/p&gt;

&lt;h2 id=&quot;使用临时文件夹中的临时文件&quot;&gt;使用临时文件夹中的临时文件&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetTempPath()&lt;/code&gt; 可以获取临时文件夹的路径：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTempPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetTempFileName()&lt;/code&gt; 可以生成一个唯一的临时文件文件名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTempFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，使用此方法需要注意，这要求临时文件夹必须存在。如果你使用了前面的方法修改了临时文件夹的地址，请务必确保文件夹存在。&lt;/p&gt;

&lt;h2 id=&quot;扩展阅读&quot;&gt;扩展阅读&lt;/h2&gt;

&lt;p&gt;如果使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetTempFileName()&lt;/code&gt; 方法创建的临时文件数量达到了 65535 个，而又不及时删除掉创建的文件的话，那么再调用此方法将抛出异常 &lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;需要注意的是，此 API 调用创建的文件数量是当前用户账户下所有程序共同累计的，其他程序用“满”了你的进程也一样会挂。当然，如果你使用的不是 .NET 的 API，而是使用原生 Win32 API，那么你可以指定临时文件名前缀，相同临时文件名前缀的程序会累计数量。而 .NET 中此 API 使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;tmp&lt;/code&gt; 前缀，所以所有的 .NET 程序会共享这 65535 个文件累计；其他程序使用其他前缀使则分别累计。&lt;/p&gt;

&lt;p&gt;另外，如果此方法无法再生成一个唯一的文件名的时候也会抛出异常。&lt;/p&gt;

&lt;p&gt;为了解决这些异常，在用户端的解决方案是删除临时文件夹。而在程序端的解决方案是 —— 本文。&lt;/p&gt;

&lt;p&gt;本文是为了和 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 一起解决一个光标问题时提出的解决方案的一种。更多关于光标问题的内容可以阅读以下链接：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/wpf/issues/696&quot;&gt;Full temporary folder will crash cursor initialization · Issue #696 · dotnet/wpf&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/wpf-%E5%85%89%E6%A0%87%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84%E6%97%B6%E5%80%99-temp-%E6%96%87%E4%BB%B6%E5%A4%B9%E6%BB%A1%E4%BA%86%E6%97%A0%E6%B3%95%E5%88%9B%E5%BB%BA&quot;&gt;WPF 光标初始化的时候 temp 文件夹满了无法创建&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/18350699/6233938&quot;&gt;c# - System.IO.IOException: “The file exists” when using System.IO.Path.GetTempFileName() - resolutions? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/4485432/6233938&quot;&gt;azure - .NET Change Temp Path - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-gettempfilenamea&quot;&gt;GetTempFileNameA function (fileapi.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.io.path.gettempfilename&quot;&gt;Path.GetTempFileName Method (System.IO) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 16 May 2019 12:03:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/redirect-environment-temp-folder.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/redirect-environment-temp-folder.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Markdown 如何在内联代码或者代码块中使用代码开始符号反引号（`）</title>
        <description>&lt;p&gt;我们都知道如何在 Markdown 中使用反引号 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 来包裹一段代码。无论是内联的代码还是单独的代码块，都需要使用它，只是个数的差别，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;```&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;那么如何能够在代码片中输入反引号（backtick）呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;方法是：&lt;strong&gt;用两个反引号来包裹&lt;/strong&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;内联代码中包含反引号&quot;&gt;内联代码中包含反引号&lt;/h2&gt;

&lt;p&gt;例如，你想输入&lt;code class=&quot;highlighter-rouge&quot;&gt;这段代码中包含`符号&lt;/code&gt;，那么你应该这么输入：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;``这段代码中包含`符号``
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;内联代码中只有反引号&quot;&gt;内联代码中只有反引号&lt;/h2&gt;

&lt;p&gt;例如，你希望输入&lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt;，那么你应该这么输入：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;`` ` ``
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，这里有 5 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 符号，其中前后各两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;``&lt;/code&gt; 是代码块的开始和结束符，中间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 则是代码块中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 符号，代码块和内容之间必须有空格。&lt;/p&gt;

&lt;h2 id=&quot;内联代码中只有反引号且有多个&quot;&gt;内联代码中只有反引号且有多个&lt;/h2&gt;

&lt;p&gt;如果你读到上面一节，你可能好奇为什么我能打出两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;``&lt;/code&gt; 符号来，是因为我输入了：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;``` `` ```
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，这里有 8 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 符号，其中前后各两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;```&lt;/code&gt; 是代码块的开始和结束符，中间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;``&lt;/code&gt; 则是代码块中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;``&lt;/code&gt; 符号，代码块和内容之间必须有空格。&lt;/p&gt;

&lt;p&gt;更多个，则以此类推。&lt;/p&gt;

&lt;h2 id=&quot;内联代码中首尾包含反引号&quot;&gt;内联代码中首尾包含反引号&lt;/h2&gt;

&lt;p&gt;有时候你希望示意 Markdown 的代码块的用法，你需要告诉别人使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&amp;lt;code&amp;gt;`&lt;/code&gt; 这样的写法。那么，你可以输入：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;`` `&amp;lt;code&amp;gt;` ``
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;`&lt;/code&gt; 符号就在内容的开始和结尾，所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;``&lt;/code&gt; 的开头和结尾也是需要输入一个空格的。&lt;/p&gt;

&lt;h2 id=&quot;代码块中的反引号&quot;&gt;代码块中的反引号&lt;/h2&gt;

&lt;p&gt;只要代码块中的反引号数量小于三个，就能直接在代码块中使用反引号而不用担心转义问题：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;`
``
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，如果反引号的数量大于或等于三个，那么代码块的包裹就需要更多的反引号了：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;```` 四个反引号开始的代码块
` 有一个
`` 有两个
``` 有三个
```` 四个反引号结束的代码块
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;反正，包括代码块的反引号可以一直重复，比里面用到的多就好了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daringfireball.net/projects/markdown/syntax&quot;&gt;Daring Fireball: Markdown Syntax Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 15 May 2019 14:09:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/markdown-code-escape-backtick.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/markdown-code-escape-backtick.html</guid>
        
        
        <category>markdown</category>
        
      </item>
    
      <item>
        <title>在编译期间使用 Roslyn/MSBuild 自带的方法/函数判断、计算和修改属性</title>
        <description>&lt;p&gt;充分利用 MSBuild 自带的方法，可以在编译期间完成大多数常见的属性转换，而不再需要自己专门写库来完成。&lt;/p&gt;

&lt;p&gt;本文介绍如何使用 MSBuild 自带的方法，并列举 MSBuild 中各种自带的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何在编译期间使用-msbuild-自带的方法&quot;&gt;如何在编译期间使用 MSBuild 自带的方法&lt;/h2&gt;

&lt;p&gt;当然，在修改编译期间的代码的时候，你可能需要提前了解项目文件相关的知识：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下是使用 MSBuild 自带方法的最简单的一个例子，执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;5-1&lt;/code&gt; 的数学运算。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&amp;gt;&lt;/span&gt;$([MSBuild]::Subtract(5, 1))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更复杂的，可能是 MSBuild 方法调用的嵌套了：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HasTrailingSlash('$(WalterlvPath)')&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(WalterlvPath.Substring(0, $([MSBuild]::Add($(WalterlvPath.Length), -1))))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上两段示例分别来自我的另外两篇博客，如果不明白，可以参考这两篇博客的内容：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-numeric-methods&quot;&gt;在 Roslyn/MSBuild 中进行基本的数学运算&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-path-trailing-slash&quot;&gt;Roslyn/MSBuild 在编译期间处理路径中的斜杠与反斜杠&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;msbuild-自带的方法&quot;&gt;MSBuild 自带的方法&lt;/h2&gt;

&lt;h3 id=&quot;数学运算&quot;&gt;数学运算&lt;/h3&gt;

&lt;p&gt;MSBuild 中数学运算的部分可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-numeric-methods&quot;&gt;在 Roslyn/MSBuild 中进行基本的数学运算&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ensuretrailingslash&quot;&gt;EnsureTrailingSlash&lt;/h3&gt;

&lt;p&gt;确保路径结尾有斜杠。&lt;/p&gt;

&lt;p&gt;可参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-path-trailing-slash&quot;&gt;Roslyn/MSBuild 在编译期间处理路径中的斜杠与反斜杠&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getdirectorynameoffileabove--getpathoffileabove&quot;&gt;GetDirectoryNameOfFileAbove &amp;amp; GetPathOfFileAbove&lt;/h3&gt;

&lt;p&gt;这两个是非常有用却又非常容易被忽视的 API，非常有必要介绍一下。&lt;/p&gt;

&lt;p&gt;可以阅读我的另一篇博客了解其用途和用法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-get-directory-name-of-file-above&quot;&gt;Roslyn/MSBuild 在编译期间从当前文件开始查找父级文件夹，直到找到包含特定文件的文件夹&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;makerelative&quot;&gt;MakeRelative&lt;/h3&gt;

&lt;p&gt;计算两个路径之间的相对路径表示。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path1&amp;gt;&lt;/span&gt;C:\Walterlv\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Path1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path2&amp;gt;&lt;/span&gt;C:\Walterlv\Demo\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Path2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath1&amp;gt;&lt;/span&gt;$([MSBuild]::MakeRelative($(Path1), $(Path2)))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath2&amp;gt;&lt;/span&gt;$([MSBuild]::MakeRelative($(Path2), $(Path1)))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath2&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath1&lt;/code&gt; 的值会计算为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo\&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath2&lt;/code&gt; 的值会计算为 &lt;code class=&quot;highlighter-rouge&quot;&gt;..\&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;valueordefault&quot;&gt;ValueOrDefault&lt;/h3&gt;

&lt;p&gt;如果赋值了，就使用所赋的值；否则使用参数指定的值：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvValue1&amp;gt;&lt;/span&gt;$([MSBuild]::ValueOrDefault('$(FooBar)', 'walterlv'))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvValue1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvValue2&amp;gt;&lt;/span&gt;$([MSBuild]::ValueOrDefault('$(WalterlvValue1)', 'lindexi'))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvValue2&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一行，因为我们没有定义任何一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;FooBar&lt;/code&gt; 的属性，所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvValue1&lt;/code&gt; 属性会计算得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 值。第二行，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvValue1&lt;/code&gt; 已经得到了一个值，所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvValue2&lt;/code&gt; 也会得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvValue1&lt;/code&gt; 的值，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt;，不会得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;lindexi&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;MSBuild 剩下的一些方法使用场景非常有限（不懂就别瞎装懂了），这里做一些简单的介绍。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$([MSBuild]::DoesTaskHostExist(string theRuntime, string theArchitecture))&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;可参见：&lt;a href=&quot;https://github.com/Microsoft/msbuild/blob/master/src/Tasks/Microsoft.Common.overridetasks&quot;&gt;msbuild/Microsoft.Common.overridetasks at master · microsoft/msbuild&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetRegistryValue&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetRegistryValueFromView&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions&quot;&gt;Property Functions - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 15 May 2019 13:41:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-property-functions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-property-functions.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>Roslyn/MSBuild 在编译期间从当前文件开始查找父级文件夹，直到找到包含特定文件的文件夹</title>
        <description>&lt;p&gt;大家在进行各种开发的时候，往往都不是写一个单纯项目就完了的，通常都会有一个解决方案，里面包含了多个项目甚至是大量的项目。我们经常会考虑输出一些文件或者处理一些文件，例如主项目的输出目录一般会选在仓库的根目录，文档文件夹一般会选在仓库的根目录。&lt;/p&gt;

&lt;p&gt;然而，我们希望输出到这些目录或者读取这些目录的项目往往在很深的代码文件夹中。如果直接通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;..\..\..&lt;/code&gt; 来返回仓库根目录非常不安全，你会数不过来的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;现在，我们有了一个好用的 API：&lt;code class=&quot;highlighter-rouge&quot;&gt;GetDirectoryNameOfFileAbove&lt;/code&gt;，可以直接找到仓库的根目录，无需再用数不清又容易改出问题的 &lt;code class=&quot;highlighter-rouge&quot;&gt;..\..\..&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;你只需要编写这样的代码，即可查找 Walterlv.DemoSolution.sln 文件所在的文件夹的完全路径了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvSolutionRoot&amp;gt;&lt;/span&gt;$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Walterlv.DemoSolution.sln))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildRoot&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而这段代码所在的文件，可能是这样的目录结构（里面的 Walterlv.DemoProject.csproj 文件）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- D:\walterlv\root
    - \src
        - \Walterlv.DemoProject
            + \Walterlv.DemoProject.csproj
        - \Walterlv.DemoProject2
        + README.md
    - \docs
    - \bin
    + \Walterlv.DemoSolution.sln
        + README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们便可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\walterlv\root&lt;/code&gt; 文件夹。&lt;/p&gt;

&lt;p&gt;另外还有一个 API &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPathOfFileAbove&lt;/code&gt;，只传入一个参数，找到文件后，返回文件的完全路径：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvSolutionRoot&amp;gt;&lt;/span&gt;$([MSBuild]::GetPathOfFileAbove(Walterlv.DemoSolution.sln))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildRoot&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终可以得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\walterlv\root\Walterlv.DemoSolution.sln&lt;/code&gt; 路径。&lt;/p&gt;

&lt;p&gt;需要注意的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;此方法&lt;strong&gt;不支持&lt;/strong&gt;通配符，也就是说不能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;*.sln&lt;/code&gt; 来找路径&lt;/li&gt;
  &lt;li&gt;此方法&lt;strong&gt;不支持&lt;/strong&gt;通过文件夹去找，也就是说不能使用我们熟知的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.git&lt;/code&gt; 等等文件夹去找路径&lt;/li&gt;
  &lt;li&gt;此方法传入的文件&lt;strong&gt;支持&lt;/strong&gt;使用路径，也就是说可以使用类似于 &lt;code class=&quot;highlighter-rouge&quot;&gt;\src\README.md&lt;/code&gt; 的方式来查找路径&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mode19.net/posts/msbuildbuildroot/&quot;&gt;Finding the Root Build Folder with MSBuild - Mode 13h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 15 May 2019 13:16:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-get-directory-name-of-file-above.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-get-directory-name-of-file-above.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>Directory Opus 使用命令编辑器添加 PowerShell / CMD / Bash 等多种终端到自定义菜单</title>
        <description>&lt;p&gt;使用 Directory Opus 替代 Windows 自带的文件资源管理器来管理你计算机上的文件可以极大地提高你的文件处理效率。&lt;/p&gt;

&lt;p&gt;本文将教你如何使用 Directory Opus 的命令编辑器功能编写一组菜单，我们将在这组菜单里面集成各种各样的终端。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;命令编辑器&quot;&gt;命令编辑器&lt;/h2&gt;

&lt;p&gt;如果你是从下面这篇文章阅读过来的，那么你现在应该正好已经打开了一个命令编辑器：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-custom-toolbar-buttons&quot;&gt;在 Directory Opus 中添加自定义的工具栏按钮提升效率&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你并没有打开命令编辑器，那么可以再阅读上面这篇文章打开一个。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;自定义工具栏&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;新建&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建按钮&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;编辑&lt;/code&gt;。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;添加三个终端命令&quot;&gt;添加三个终端命令&lt;/h2&gt;

&lt;p&gt;请参考 &lt;a href=&quot;/post/directory-opus-integrate-with-tortoise-git&quot;&gt;Directory Opus 使用命令编辑器集成 TortoiseGit 的各种功能&lt;/a&gt; 一文中添加自定义按钮的方法，同样地添加另外的三个按钮。这里，我将三个不同终端的添加参数放到了下面，你可以参考添加：&lt;/p&gt;

&lt;h3 id=&quot;powershell-core&quot;&gt;PowerShell Core&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-00-43.png&quot; alt=&quot;PowerShell Core&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;git-bash&quot;&gt;Git Bash&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-01-00.png&quot; alt=&quot;Git Bash&quot; /&gt;&lt;/p&gt;

&lt;p&gt;特别注意，在函数一栏的参数中，我们传入了一个路径参数。那个参数的末尾必须加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;\.&lt;/code&gt;，否则 Git Bash 是无法启动的。&lt;/p&gt;

&lt;h3 id=&quot;cmd&quot;&gt;CMD&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-01-15.png&quot; alt=&quot;CMD&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;添加一个菜单&quot;&gt;添加一个菜单&lt;/h2&gt;

&lt;p&gt;在添加完上面的三个命令之后，你应该可以在工具栏上看到三个可以启动不同终端的窗口。现在我们需要将它们都集成到一个菜单中。&lt;/p&gt;

&lt;h3 id=&quot;新建一个菜单&quot;&gt;新建一个菜单&lt;/h3&gt;

&lt;p&gt;在工具栏上空白处右键，&lt;code class=&quot;highlighter-rouge&quot;&gt;新建&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建菜单&lt;/code&gt;，然后右键，编辑这个菜单：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-03-13.png&quot; alt=&quot;新建菜单&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-03-45.png&quot; alt=&quot;编辑新建菜单&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，我们又可以弹出一个命令编辑器窗口，由于菜单本身不打开命令只会显示子菜单，所以里面非常简单。设置图标和显示的文字即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-05-14.png&quot; alt=&quot;菜单的命令编辑窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，依然保持在工具栏的编辑状态，将我们前面创建的三个按钮依次拖入菜单中即可形成一个菜单：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-12-41.png&quot; alt=&quot;拖入到菜单中&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;新建一个菜单按钮&quot;&gt;新建一个菜单按钮&lt;/h3&gt;

&lt;p&gt;在工具栏上空白处右键，&lt;code class=&quot;highlighter-rouge&quot;&gt;新建&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建菜单按钮&lt;/code&gt;，这样的菜单除了显示子项之外，还可以执行命令。然后右键，编辑这个菜单：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-18-42.png&quot; alt=&quot;新建菜单按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-22-19.png&quot; alt=&quot;编辑新建菜单按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，我们又可以弹出一个命令编辑器窗口，如果我们不打算让这个菜单按钮额外具备一些功能，则值设置图标和文字即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-19-30.png&quot; alt=&quot;菜单按钮的命令编辑窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，我更期望在这里将你希望默认打开的终端参数设进去，比较方便一些。&lt;/p&gt;

&lt;p&gt;然后，依然保持在工具栏的编辑状态，将我们前面创建的三个按钮依次拖入菜单中即可形成一个菜单：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-17-25-13.png&quot; alt=&quot;拖入到菜单按钮中&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;后续&quot;&gt;后续&lt;/h2&gt;

&lt;p&gt;关于命令设置的详细细节，可以继续阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-integrate-with-tortoise-git&quot;&gt;Directory Opus 使用命令编辑器集成 TortoiseGit 的各种功能&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最后，在自定义完按钮之后，不要忘了关闭最开始弹出来的“自定义工具栏”的对话框。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-41-23.png&quot; alt=&quot;“自定义工具栏”对话框&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 15 May 2019 09:27:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/directory-opus-integrate-with-terminals.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/directory-opus-integrate-with-terminals.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>C# 8.0 中开启默认接口实现</title>
        <description>&lt;p&gt;当你升级到 C# 8.0 和 .NET Core 3.0 之后，你就可以开始使用默认接口实现的功能了。&lt;/p&gt;

&lt;p&gt;从现在开始，你可以在接口里面添加一些默认实现的成员，避免在接口中添加成员导致大量对此接口的实现崩溃。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;最低要求&quot;&gt;最低要求&lt;/h2&gt;

&lt;p&gt;要写出并且正常使用接口的默认实现，你需要：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C# 8.0&lt;/li&gt;
  &lt;li&gt;.NET Core 3.0&lt;/li&gt;
  &lt;li&gt;Visual Studio 2019 Preview (16.1 以上版本)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;下载安装-visual-studio-2019-preview-版本&quot;&gt;下载安装 Visual Studio 2019 Preview 版本&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;前往下载安装 &lt;a href=&quot;https://visualstudio.microsoft.com/vs/preview/&quot;&gt;Visual Studio Preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;开启-net-core-30-的支持&quot;&gt;开启 .NET Core 3.0 的支持&lt;/h3&gt;

&lt;p&gt;对于预览版的 Visual Studio 2019 来说，.NET Core 的预览版是默认打开且无法关闭的，所以不需要关心。&lt;/p&gt;

&lt;h3 id=&quot;开启-c-80-支持&quot;&gt;开启 C# 8.0 支持&lt;/h3&gt;

&lt;p&gt;请设置你项目的属性，修改 C# 语言版本为 8.0（对于预览版的语言来说，这是必要的）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-14-28-13.png&quot; alt=&quot;修改语言版本&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或者直接修改你的项目文件，加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;LangVersion&lt;/code&gt; 属性的设置，设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;8.0&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;8.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;默认接口实现&quot;&gt;默认接口实现&lt;/h2&gt;

&lt;h3 id=&quot;以前的做法&quot;&gt;以前的做法&lt;/h3&gt;

&lt;p&gt;比如，我们现在有下面这样一个简单的接口：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IWalterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个接口被大量实现了。&lt;/p&gt;

&lt;p&gt;现在，我们需要在接口中新增一个方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;DouBPrint&lt;/code&gt;，其作用是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Print&lt;/code&gt; 方法进行标准化，避免各种不同实现带来的标准差异。于是我们新增一个方法：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public interface IWalterlv
    {
        void Print(string text);

++      void DouBPrint(string text);
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而我们都知道，这样的修改是破坏性的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;会使得所有实现这个接口的代码全部失败（无法编译通过，或者运行时抛出异常）&lt;/li&gt;
  &lt;li&gt;我们依然很难将接口的实现标准化，靠文档来规约&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;默认接口实现-1&quot;&gt;默认接口实现&lt;/h3&gt;

&lt;p&gt;那么现在，我们可以这样来新增此方法：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public interface IWalterlv
    {
        void Print(string text);
        
&lt;span class=&quot;gd&quot;&gt;--      void DouBPrint(string text);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      public void DouBPrint(string text) =&amp;gt; Print($&quot;Walterlv 逗比 {text}&quot;);
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在使用此方法来定义此接口中的方法后，那些没来得及实现此方法的类型也可以编译通过并获得标准化的实现。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IWalterlv&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DouBPrint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWalterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型来说，实现也是可以的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWalterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DouBPrint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Walterlv 逗比 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;静态字段和方法&quot;&gt;静态字段和方法&lt;/h3&gt;

&lt;p&gt;除此之外，在接口中还可以编写静态字段和静态方法，这可以用来统一接口中的一些默认实现。&lt;/p&gt;

&lt;p&gt;意味着，如果类没有实现接口中带有默认实现的方法，那么具有默认的实现；而如果类中打算实现接口中的带有默认实现的方法，那么也可以调用接口中的静态方法来进行实现。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public interface IWalterlv
    {
        void Print(string text);

--      public void DouBPrint(string text) =&amp;gt; Print($&quot;Walterlv 逗比 {text}&quot;);
&lt;span class=&quot;gi&quot;&gt;++      public void DouBPrint(string text) =&amp;gt; DefaultDouBPrint(this, text);
++
++      private static readonly string _name = &quot;walterlv&quot;;
++
++      protected static void DefaultDouBPrint(IWalterlv walterlv, string text)
++          =&amp;gt; walterlv.Print($&quot;{_name} 逗比 {text}&quot;);
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，对于实现方，则需要使用接口名来调用接口中的静态成员：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public class Foo : IWalterlv
    {
        public void Print(string text)
        {
        }

--      public void DouBPrint(string text) =&amp;gt; Print($&quot;Walterlv 逗比 {text}&quot;);
&lt;span class=&quot;gi&quot;&gt;++      public void DouBPrint(string text)
++      {
++          // Do Other things.
++          IWalterlv.DefaultDouBPrint(this, text);
++      }
++  }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/&quot;&gt;Default implementations in interfaces - .NET Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/visualstudio/visual-studio-2019-version-16-1-preview-3/&quot;&gt;Visual Studio 2019 version 16.1 Preview 3 - The Visual Studio Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-members-versions&quot;&gt;Safely update interfaces using default interface members in C# - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 15 May 2019 06:57:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/default-interface-members-practise.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/default-interface-members-practise.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Directory Opus 使用命令编辑器集成 TortoiseGit 的各种功能</title>
        <description>&lt;p&gt;使用 Directory Opus 替代 Windows 自带的文件资源管理器来管理你计算机上的文件可以极大地提高你的文件处理效率。&lt;/p&gt;

&lt;p&gt;本文将教你如何使用 Directory Opus 的命令编辑器功能创建一个命令——跟 TortoiseGit 进行集成。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;命令编辑器&quot;&gt;命令编辑器&lt;/h2&gt;

&lt;p&gt;如果你是从下面这篇文章阅读过来的，那么你现在应该正好已经打开了一个命令编辑器：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-custom-toolbar-buttons&quot;&gt;在 Directory Opus 中添加自定义的工具栏按钮提升效率&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你并没有打开命令编辑器，那么可以再阅读上面这篇文章打开一个。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;自定义工具栏&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;新建&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建按钮&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;编辑&lt;/code&gt;。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;寻找命令&quot;&gt;寻找命令&lt;/h2&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/view-process-info-using-task-manager&quot;&gt;Windows 系统上使用任务管理器查看进程的各项属性&lt;/a&gt; 一文中告诉大家可以在任务管理器中查看某个正在运行中的进程的命令行参数，于是我们可以通过这样的方式得知如何集成 TortoiseGit 的各项功能。&lt;/p&gt;

&lt;p&gt;比如，我们在一个文件夹中从文件资源管理器中右键，选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Git 克隆...&lt;/code&gt;，等待打开一个 TortoiseGit 的克隆窗口。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-08-41-46.png&quot; alt=&quot;TortoiseGit 的 Git 克隆菜单项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，我们去任务管理器中查看此任务的命令行参数：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-08-50-10.png&quot; alt=&quot;TortoiseGit 克隆的命令行参数&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TortoiseGitProc.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/command:clone&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/path:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D:\walterlv&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/hwnd:0000000000161264&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么接下来，我们将这些信息逐一填入到命令编辑器窗格中。&lt;/p&gt;

&lt;h2 id=&quot;填写命令&quot;&gt;填写命令&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-45-44.png&quot; alt=&quot;命令编辑器&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;函数&quot;&gt;函数&lt;/h3&gt;

&lt;p&gt;函数一栏，如果你只是简单地希望启动一个程序传入参数的话，那么称之为“启动程序”可能更合适一些。&lt;/p&gt;

&lt;p&gt;但是，我依然倾向于在后面继续保持“函数”的称呼，因为这才能体现出 Directory Opus 自定义按钮命令的强大。所以如果你后面看到我提及“函数”，那么指的就是这里的功能。&lt;/p&gt;

&lt;p&gt;Directory Opus 相比于 Total Commander 的一大特点便是其鼠标支持，这在“函数”一栏的填写中也有所体现。你可以在函数一栏的最右侧看到一个文件夹图标，点击之后可以选择你想启动的程序。&lt;/p&gt;

&lt;p&gt;现在，我们通过这个按钮找到 TortoiseGitProc.exe 程序，于是我们就可以在“函数”一栏中自动填入 TortoiseGitProc.exe 的程序路径。&lt;/p&gt;

&lt;p&gt;我们在任务管理器中看到了应该给 &lt;code class=&quot;highlighter-rouge&quot;&gt;TortoiseGitProc.exe&lt;/code&gt; 传入的参数，所以我们直接在此文本框的后面继续添加参数。添加后的整个文本框中的内容应该是下面这样的：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/command:clone&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/path:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{sourcepath}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里出现了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;{sourcepaht}&lt;/code&gt;，这是一个表示当前路径的变量，稍后会作详细的说明。&lt;/p&gt;

&lt;h3 id=&quot;图标说明显示说明提示信息&quot;&gt;图标、说明、显示说明、提示信息&lt;/h3&gt;

&lt;p&gt;我们在“函数”一栏中添加了一个可以启动的程序之后，Directory Opus 的命令编辑窗口会自动帮我们从主程序中获取一个可以显示的图标。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-20-28.png&quot; alt=&quot;选择了程序之后，出现了图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击一下这个图标，可以选择此程序的其他图标。我们现在正在做的是一个 Git Clone 的按钮，所以我们选择一个表示克隆仓库的图标：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-09-59-51.png&quot; alt=&quot;选择图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接着，我们需要进行一些基础的设置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;图标：将显示大图标打勾，可以使用更大更清晰的图标，这对于我这种 UI 党来说会更加友好。&lt;/li&gt;
  &lt;li&gt;说明：这是最终会出现在按钮上的文字。我填写了“Git 克隆…”，后面的三个点在 Windows 系统中是一种交互惯例，表示点击后还需要用户给出额外的信息才能完成指定的任务。&lt;/li&gt;
  &lt;li&gt;显示说明：说明文字会出现在图标的哪个方向。我选择了右侧，这跟 Directory Opus 上的多数已有工具栏是保持一致的。&lt;/li&gt;
  &lt;li&gt;提示信息：上你把鼠标移动到按钮上的时候，将显示的工具提示说明。可以使用比较长的一段话清晰地说明这个按钮是干什么用的。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-22-53.png&quot; alt=&quot;设置了基础属性的命令&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;开始于&quot;&gt;开始于&lt;/h3&gt;

&lt;p&gt;开始于，指的是点击此按钮运行我们指定的“函数”时，如果函数打开了一个进程，那么此进程的工作路径是什么。&lt;/p&gt;

&lt;p&gt;我们先填入 &lt;code class=&quot;highlighter-rouge&quot;&gt;{sourcepath}&lt;/code&gt;。这里，我们再次使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;{sourcepath}&lt;/code&gt; 这个变量。稍后会进行说明。&lt;/p&gt;

&lt;p&gt;实际上到此为止，如果你按下“确定”按钮，你将在工具栏上看见一个“Git 克隆…”按钮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-38-32.png&quot; alt=&quot;“Git 克隆...”按钮&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;高级&quot;&gt;高级&lt;/h2&gt;

&lt;p&gt;如果你没有关闭此窗口，那么点击“高级…”，我们将打开高级的命令编辑器。现在我们可以注意到下面出现了一个非常大的函数编辑窗口，而此前的“函数”、“开始于”、“运行”选项都消失了。这是因为此函数编辑窗口涵盖了消失的这些按钮的所有功能，而且更为强大，因为可以使用更多种类的命令。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-23-41.png&quot; alt=&quot;高级命令编辑器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-14-01.png&quot; alt=&quot;函数类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;留意命令编辑器中的命令，我们此前用鼠标点击的操作实际上对应了两行命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sourcepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C:\Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Files\TortoiseGit\bin\TortoiseGitProc.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/command:clone&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/path:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{sourcepath}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一行是将路径转换到 &lt;code class=&quot;highlighter-rouge&quot;&gt;{sourcepath}&lt;/code&gt; 变量所指示的路径中，第二行是启动一个程序并传入适当的参数。&lt;/p&gt;

&lt;p&gt;而这个参数是什么意思呢？如何可以输入呢？&lt;/p&gt;

&lt;p&gt;请点击命令编辑器上面的“参数”按钮，这时会弹出一个菜单，对各种各样可以输入的参数放在一起进行了分类存放。&lt;/p&gt;

&lt;p&gt;因为我们要克隆 Git 仓库需要现在 Directory Opus 里面先进入一个文件夹，然后将 Git 仓库克隆到此仓库中，所以我们实际上是希望拿到 Directory Opus 当前正在浏览的文件夹。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;{sourcepath}&lt;/code&gt; 表示正在操作的源路径，而正在操作的源路径就是 Directory Opus 的当前文件夹（如果你有多个文件夹窗格，则是当前激活的那个窗口所在的文件夹）。所以我们选用了此参数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-26-21.png&quot; alt=&quot;Directory Opus 可以输入的参数&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;最后一步&quot;&gt;最后一步&lt;/h2&gt;

&lt;p&gt;在自定义完按钮之后，不要忘了关闭最开始弹出来的“自定义工具栏”的对话框。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-15-10-41-23.png&quot; alt=&quot;“自定义工具栏”对话框&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 15 May 2019 02:41:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/directory-opus-integrate-with-tortoise-git.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/directory-opus-integrate-with-tortoise-git.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>在 Directory Opus 中添加自定义的工具栏按钮提升效率</title>
        <description>&lt;p&gt;使用 Directory Opus 替代 Windows 自带的文件资源管理器来管理你计算机上的文件可以极大地提高你的文件处理效率。&lt;/p&gt;

&lt;p&gt;Directory Opus 自定义的工具栏按钮可以执行非常复杂的命令，所以充分利用自定义工具栏按钮的功能可以更大程度上提升工作效率。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;directory-opus-的工具栏&quot;&gt;Directory Opus 的工具栏&lt;/h2&gt;

&lt;p&gt;这是我的 Directory Opus 的界面（暂时将左侧的树关掉了）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-53-32.png&quot; alt=&quot;Directory Opus&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下图是我目前添加的一些工具栏按钮：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-52-05.png&quot; alt=&quot;Directory Opus 的工具栏按钮&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;自定义工具栏按钮&quot;&gt;自定义工具栏按钮&lt;/h2&gt;

&lt;p&gt;自定义的方法是，点击顶部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;自定义工具栏&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-36-51.png&quot; alt=&quot;自定义工具栏菜单&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，会弹出自定义工具栏的对话框，并且所有可以被定制的工具栏现在都会进入编辑状态等待着我们对其进行编辑：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-38-31.png&quot; alt=&quot;正在自定义工具栏&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;添加一个自定义按钮&quot;&gt;添加一个自定义按钮&lt;/h2&gt;

&lt;p&gt;你并不需要在自定义工具栏对话框上进行任何操作，只需要在一个现有的工具栏上点击右键，然后点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;新建&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;新建按钮&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-41-16.png&quot; alt=&quot;新建按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，你会看到一个新的按钮已经出现在了工具栏上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-44-51.png&quot; alt=&quot;新建的按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，在此按钮上点击右键，“编辑”，就打开了 Directory Opus 的命令编辑器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-19-45-44.png&quot; alt=&quot;命令编辑器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来，我们的操作就进入了本文的主要内容，也是最复杂的一部分内容了。&lt;/p&gt;

&lt;h2 id=&quot;命令编辑器&quot;&gt;命令编辑器&lt;/h2&gt;

&lt;p&gt;要定义一个能够极大提升效率的按钮，命令编辑器中的多数框我们都是要使用的。&lt;/p&gt;

&lt;p&gt;接下来我会通过两个示例来说明如何使用这个命令编辑器。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-integrate-with-tortoise-git&quot;&gt;Directory Opus 使用命令编辑器集成 TortoiseGit 的各种功能&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/directory-opus-integrate-with-terminals&quot;&gt;Directory Opus 使用命令编辑器添加 PowerShell / CMD / Bash 等多种终端到自定义菜单&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在自定义完按钮之后，不要忘了关闭最开始弹出来的“自定义工具栏”的对话框。&lt;/p&gt;

&lt;h2 id=&quot;一切皆命令&quot;&gt;一切皆命令&lt;/h2&gt;

&lt;p&gt;在阅读上面的博客定义完一些自己的命令之后，你再观察 Directory Opus 的其他工具栏按钮，包括左上角的菜单，你会发现其实 Directory Opus 中所有的功能按钮和菜单都是使用相同的机制建立起来的。&lt;/p&gt;

&lt;p&gt;一切皆命令。&lt;/p&gt;

&lt;p&gt;这些命令组成了 Directory Opus 主界面的绝大多数功能。&lt;/p&gt;
</description>
        <pubDate>Wed, 15 May 2019 02:41:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/directory-opus-custom-toolbar-buttons.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/directory-opus-custom-toolbar-buttons.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>让 Directory Opus 支持 Windows 10 的暗色主题</title>
        <description>&lt;p&gt;使用 Directory Opus 替代 Windows 自带的文件资源管理器来管理你计算机上的文件可以极大地提高你的文件处理效率。&lt;/p&gt;

&lt;p&gt;由于我自己的 Windows 10 系统使用的是暗色主题，所以我希望 Directory Opus 也能搭配我系统的纯暗色主题。&lt;/p&gt;

&lt;p&gt;本文介绍如何将 Directory Opus 打造成搭配 Windows 10 的暗色主题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;directory-opus-主题支持&quot;&gt;Directory Opus 主题支持&lt;/h2&gt;

&lt;p&gt;Directory Opus 在安装完之后的默认主题样式是下面这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-15-55-57.png&quot; alt=&quot;Directory Opus 默认主题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，我的 Windows 10 的主要界面都是暗黑色的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-15-58-02.png&quot; alt=&quot;Windows 10 中的主题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么，请在 Directory Opus 顶部菜单中选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;主题&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-15-56-37.png&quot; alt=&quot;设置 -&amp;gt; 主题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后点击左下角的下载主题去网上下载一款主题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-15-58-57.png&quot; alt=&quot;主题选择页面&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;windows-10-暗色风格的主题&quot;&gt;Windows 10 暗色风格的主题&lt;/h2&gt;

&lt;p&gt;你可以直接使用下面的链接下载 Windows 10 暗色风格的主题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://resource.dopus.com/t/simple-windows-10-dark-theme/30055&quot;&gt;Simple Windows 10 Dark Theme - Downloads / Themes - Directory Opus Resource Centre&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然后，依然进入我们一开始说的 &lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;主题&lt;/code&gt; 对话框中，导入刚刚我们下载好的主题：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-01-31.png&quot; alt=&quot;导入主题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“应用”，随后 Directory Opus 会重新启动，你将看到全新的 Windows 10 暗色风格主题。&lt;/p&gt;

&lt;h2 id=&quot;微调主题样式&quot;&gt;微调主题样式&lt;/h2&gt;

&lt;p&gt;等等！为什么重启之后看起来样式怪怪的？有一些文件的文字其实在暗色主题下看不太清。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-03-10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这当然是主题设计者没有考虑到所有的情况导致的，实际上你下载的任何一款主题可能都有各种考虑不周的情况，那么如何修复这些考虑不周的细节呢？&lt;/p&gt;

&lt;p&gt;我们需要前往 &lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; 中微调这些细节。在“选项”对话框中，选择“颜色和字体”标签。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-04-43.png&quot; alt=&quot;设置 -&amp;gt; 选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-05-22.png&quot; alt=&quot;颜色和字体&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;微调文件组标题&quot;&gt;微调文件组标题&lt;/h3&gt;

&lt;p&gt;在我一开始的暗色主题应用后，我们注意到我的文件是分组的，组标题是深蓝色，看不清。于是修改“文件组标题”中的颜色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-07-07.png&quot; alt=&quot;文件组标题&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;微调压缩的文件和文件夹&quot;&gt;微调压缩的文件和文件夹&lt;/h3&gt;

&lt;p&gt;另外，我的多数文件是加入了 NTFS 压缩的，这部分文件被主题设置了很难看清的深紫色，我将它改为其他的颜色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-08-08.png&quot; alt=&quot;文件和文件夹&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;微调其他部件&quot;&gt;微调其他部件&lt;/h2&gt;

&lt;p&gt;里面还有大量可以微调的部件，如果你遇到了不符合你要求的颜色设置，则将其修改即可。&lt;/p&gt;

&lt;p&gt;以下是我进行了微调之后的主题效果预览：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-10-10.png&quot; alt=&quot;修改后的主题效果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;还原成默认的主题&quot;&gt;还原成默认的主题&lt;/h2&gt;

&lt;p&gt;你可能会注意到在主题选择窗格中只有我们刚刚下载的那一个主题，我们不能选择回默认的主题样式。那如果一个主题被我们改残了，或者就是想重新体验原生效果的时候该如何做呢？&lt;/p&gt;

&lt;p&gt;我们依然需要进入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; 中，然后选择“颜色和字体”标签。&lt;/p&gt;

&lt;p&gt;这时，选择顶部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;文件&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;重置该页到默认值&lt;/code&gt;。于是，我们的主题就会还原到最初没有修改任何字体和颜色的版本。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-14-16-13-57.png&quot; alt=&quot;重置主题的设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果主题涉及到图标等其他资源，也需要进入对应的标签页然后还原对应标签页的设置。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://resource.dopus.com/c/downloads/themes&quot;&gt;Latest Themes topics - Directory Opus Resource Centre&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://resource.dopus.com/t/simple-windows-10-dark-theme/30055&quot;&gt;Simple Windows 10 Dark Theme - Downloads / Themes - Directory Opus Resource Centre&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://resource.dopus.com/t/plain-default-theme/1169&quot;&gt;Plain / Default Theme - Themes - Directory Opus Resource Centre&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 14 May 2019 08:17:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/directory-opus-customize-theme.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/directory-opus-customize-theme.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>通过分析 WPF 的渲染脏区优化渲染性能</title>
        <description>&lt;p&gt;本文介绍通过发现渲染脏区来提高渲染性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;脏区-dirty-region&quot;&gt;脏区 Dirty Region&lt;/h2&gt;

&lt;p&gt;在计算机图形渲染中，可以每一帧绘制全部的画面，但这样对计算机的性能要求非常高。&lt;/p&gt;

&lt;p&gt;脏区（Dirty Region）的引入便是为了降低渲染对计算机性能的要求。每一帧绘制的时候，仅仅绘制改变的部分，在软件中可以节省大量的渲染资源。而每一帧渲染时，改变了需要重绘的部分就是脏区。&lt;/p&gt;

&lt;p&gt;以下是我的一款 WPF 程序 &lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard&quot;&gt;Walterlv.CloudKeyboard&lt;/a&gt; 随着交互的进行不断需要重绘的脏区。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-07-uncontrollable-dirty-region.gif&quot; alt=&quot;较多的脏区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，脏区几乎涉及到整个界面，而且刷新非常频繁。这显然对渲染性能而言是不利的。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;当然这个程序很小，就算一直全部重新渲染性能也是可以接受的。&lt;/em&gt;不过当程序中存在比较复杂的部分，如大量的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt; 以及 3D 图形的时候，重新渲染这一部分将带来严重的性能问题。&lt;/p&gt;

&lt;h2 id=&quot;wpf-性能套件&quot;&gt;WPF 性能套件&lt;/h2&gt;

&lt;p&gt;先下载 WPF 性能套件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceToolKit_amd64/wpt_x64.msi&quot;&gt;下载 Performance Profiling Tools for Windows Presentation Foundation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://download.microsoft.com/download/1/8/9/189A7832-49D8-4978-85E8-3DFFF44E6C04/WpfPerf_timezone_patch.msp&quot;&gt;下载 补丁&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;脏区监视&quot;&gt;脏区监视&lt;/h2&gt;

&lt;p&gt;启动 WPF Performance Suite，选择工具 Perforator，然后在 Action 菜单中启动一个待分析的 WPF 进程。虽然工具很久没有更新，但依然可以支持基于 .NET Core 3 版本的 WPF 程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-08-09-00-36.png&quot; alt=&quot;启动一个进程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当程序运行起来后，可以看到 WPF 程序的各种性能数据图表。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-08-08-59-08.png&quot; alt=&quot;WPF 性能收集工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Show dirty-region update overlay&lt;/code&gt; 选项打勾即可看到本文一开始的脏区叠加层的显示。&lt;/p&gt;

&lt;p&gt;与脏区有关的选项有三个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Show dirty-region update overlay
    &lt;ul&gt;
      &lt;li&gt;显示脏区叠加层，每一次脏区出现需要重新渲染时会叠加一层新的半透明颜色。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Disable dirty region support
    &lt;ul&gt;
      &lt;li&gt;禁用脏区支持。这时，每次渲染都将重绘整个窗口。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Clear back-buffer before rendering
    &lt;ul&gt;
      &lt;li&gt;每次重绘之前都将清除之前所有的绘制，使用此选项，你可以迅速找到界面中频繁刷新的部分，而重绘频率不高的部分多数时候都是纯黑。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;优化脏区重绘&quot;&gt;优化脏区重绘&lt;/h2&gt;

&lt;p&gt;一开始的程序中，因为我使用了模拟 UWP 的高光效果，导致大量的控件在重绘高光部分，这是导致每一帧都在重新渲染的罪魁祸首。&lt;/p&gt;

&lt;p&gt;于是我将高光渲染关闭，脏区的重新渲染将仅仅几种在控件样式改变的时候（例如焦点改变）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-08-controllable-dirty-region.gif&quot; alt=&quot;稍微正常一点的脏区&quot; /&gt;&lt;/p&gt;

&lt;p&gt;光照效果可以参见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- ## 性能优化建议

如果你希望重绘的脏区面积更少，那么建议：

1. 不要频繁修改面积太大的元素（例如像我这样调整一个大面积元素的边框颜色，这样整个大面积元素都会成为脏区）
1. 尽量将频繁修改的大面积元素拆分成多个小面积 Visual --&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/aa969767(v=vs.110)&quot;&gt;WPF Performance Suite - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 13 May 2019 01:50:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-rendering-dirty-region.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-rendering-dirty-region.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Roslyn/MSBuild 在编译期间处理路径中的斜杠与反斜杠</title>
        <description>&lt;p&gt;本文介绍如何在项目文件 csproj，或者 MSBuild 的其他文件（props、targets）中处理路径中的斜杠与反斜杠。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;路径中的斜杠与反斜杠&quot;&gt;路径中的斜杠与反斜杠&lt;/h2&gt;

&lt;p&gt;我们都知道文件路径的层级之间使用斜杠（&lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;）或者反斜杠（&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;）来分隔，具体使用哪一个取决于操作系统。本文不打算对具体使用哪一种特别说明，不过示例都是使用 Windows 操作系统中的反斜杠（&lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;对于一个文件夹的路径，末尾无论是否有反斜杠都不会影响找到这个路径对应的文件夹，但是有时我们又因为一些特殊的用途需要知道末尾的反斜杠的情况。&lt;/p&gt;

&lt;p&gt;在 MSBuild 中，通常有一个在文件夹路径末尾添加反斜杠 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 的惯例，这样可以直接使用属性拼接来形成新的路径而不用担心路径中的不同层级的文件夹会连接在一起。&lt;/p&gt;

&lt;p&gt;例如属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath1&lt;/code&gt; 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt;，属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath2&lt;/code&gt; 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt;。为了确保两个可以直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(WalterlvPath1)$(WalterlvPath2)&lt;/code&gt; 来拼接，我们需要在这两个属性的末尾都加上反斜杠 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt;。不过由于需要照顾到各式各样的开发者，包括大多数的那些从来不看文档的开发者，我们需要进行本文所述的处理。&lt;/p&gt;

&lt;h2 id=&quot;判断路径末尾是否有斜杠或反斜杠&quot;&gt;判断路径末尾是否有斜杠或反斜杠&lt;/h2&gt;

&lt;p&gt;如果路径末尾没有反斜杠，那么我们现在就添加一个反斜杠。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!HasTrailingSlash('$(WalterlvPath)')&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(WalterlvPath)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath&lt;/code&gt; 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt;，则会在这一个属性重新计算值的时候变成 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\&lt;/code&gt;；如果已经是 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\&lt;/code&gt;，则不会重新计算值，于是保持不变。&lt;/p&gt;

&lt;h2 id=&quot;确保路径末尾有斜杠或反斜杠&quot;&gt;确保路径末尾有斜杠或反斜杠&lt;/h2&gt;

&lt;p&gt;另外，也有方法可以不用做判断，直接给末尾根据情况加上反斜杠。&lt;/p&gt;

&lt;p&gt;通过调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild.EnsureTrailingSlash&lt;/code&gt; 可以确保路径的末尾已经有一个斜杠或者反斜杠。&lt;/p&gt;

&lt;p&gt;例如，我们有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath&lt;/code&gt; 属性，值可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug&lt;/code&gt; 也有可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug\&lt;/code&gt;，那么可以统一将其处理成 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\Debug\&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath&amp;gt;&lt;/span&gt;$([MSBuild]::EnsureTrailingSlash('$(WalterlvPath)'))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;确保路径末尾没有斜杠或反斜杠&quot;&gt;确保路径末尾没有斜杠或反斜杠&lt;/h2&gt;

&lt;p&gt;正常情况下，我们都是需要 MSBuild 中文件夹路径的末尾有斜杠或者反斜杠。不过，当我们需要将这个路径作为命令行参数的一部分传给一个可执行程序的时候，就没那么容易了。&lt;/p&gt;

&lt;p&gt;因为为了确保路径中间的空格不会被命令行参数解析给分离，我们需要在路径的周围加上引号。具体来说，是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;quot;&lt;/code&gt; 转义字符来添加引号：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;quot;$(WalterlvDemoTool)&amp;amp;quot; --option &amp;amp;quot;$(WalterlvPath)&amp;amp;quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 Target 是我在另一篇博客中的简化版本：&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;但是这样，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath&lt;/code&gt; 中存在反斜杠，那么这个命令行将变成这样：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;&amp;gt; &quot;walterlv.tool.exe&quot; --option &quot;bin\&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&quot;&lt;/code&gt; 将使得引号成为路径中的一部分，而这样的路径是不合法的路径！&lt;/p&gt;

&lt;p&gt;我们可以确保路径的末尾添加一个空格来避免将引号也解析成命令行的一部分：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;quot;$(WalterlvDemoTool)&amp;amp;quot; --option &amp;amp;quot;$([MSBuild]::EnsureTrailingSlash('$(BasePathInInstaller)')) &amp;amp;quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过也可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SubString&lt;/code&gt; 来对末尾的斜杠或反斜杠进行裁剪。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvPath&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HasTrailingSlash('$(WalterlvPath)')&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(WalterlvPath.Substring(0, $([MSBuild]::Add($(WalterlvPath.Length), -1))))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvPath&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解释一下这里 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(WalterlvPath.Substring(0, $([MSBuild]::Add($(WalterlvPath.Length), -1))))&lt;/code&gt; 所做的事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(WalterlvPath.Length)&lt;/code&gt; 计算出 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvPath&lt;/code&gt; 属性的长度；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$([MSBuild]::Add(length, -1))&lt;/code&gt; 调用加法，将前面计算所得的长度 -1，用于提取无斜杠或反斜杠的路径长度。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(WalterlvPath.Substring(0, length-1)&lt;/code&gt; 将路径字符串取出子串。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里的解释里面，&lt;code class=&quot;highlighter-rouge&quot;&gt;length&lt;/code&gt; 只是表意，并不是为了编译通过。要编译的代码还是上面代码块中的完整代码。&lt;/p&gt;

&lt;p&gt;更多关于在 Roslyn/MSBuild 中进行数学运算的内容，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-numeric-methods&quot;&gt;在 Roslyn/MSBuild 中进行数学运算 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 May 2019 06:47:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-path-trailing-slash.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-path-trailing-slash.html</guid>
        
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>在 Roslyn/MSBuild 中进行基本的数学运算</title>
        <description>&lt;p&gt;在任何一种编程语言中，做基本的数学运算都是非常容易的事情。不过，不知道 .NET 项目的项目文件 csproj 文件中进行数学运算就不像一般的编程语言那样直观了，毕竟这不是一门语言，而只是一种项目文件格式而已。&lt;/p&gt;

&lt;p&gt;本文介绍如何在 Roslyn/MSBuild 的项目文件中使用基本的数学运算。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;roslynmsbuild-中的数学运算&quot;&gt;Roslyn/MSBuild 中的数学运算&lt;/h2&gt;

&lt;p&gt;在 MSBuild 中，数学运算需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 内建的方法调用来实现。&lt;/p&gt;

&lt;p&gt;你只需要给 MSBuild 中那些数学计算方法中传入看起来像是数字的属性，就可以真的计算出数字出来。&lt;/p&gt;

&lt;h3 id=&quot;加减乘除模&quot;&gt;加减乘除模&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 两个数相加，实现 a + b&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Subtract&lt;/code&gt; 第一个数减去第二个数，实现 a - b&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Multiply&lt;/code&gt; 两个数相乘，实现 a * b&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Divide&lt;/code&gt; 第一个数除以第二个数，实现 a / b&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Modulo&lt;/code&gt; 第一个数与第二个数取模，实现 a % b&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而具体到 MSBuild 中的使用，则是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 计算 5 - 1 的数学运算结果 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&amp;gt;&lt;/span&gt;$([MSBuild]::Subtract(5, 1))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 取出 Walterlv 属性的字符串值，然后计算其长度减去 1，将数学运算结果存入 Walterlv2 属性中 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&amp;gt;&lt;/span&gt;walterlv is a 逗比&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv2&amp;gt;&lt;/span&gt;$([MSBuild]::Subtract($(Walterlv.Length), 1))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;不要试图在-msbuild-中使用传统的数学运算符号&quot;&gt;不要试图在 MSBuild 中使用传统的数学运算符号&lt;/h2&gt;

&lt;p&gt;不同于一般编程语言可以写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;，如果你直接在项目文件中使用这样的符号来进行数学计算，要么你将得到一个数学运算的字符串，要么你将得到编译错误。&lt;/p&gt;

&lt;p&gt;例如，如果你在你的项目文件中写了下面这样的代码，那么无一例外全部不能得到正确的数学运算结果。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个属性将得到一个 “1 + 1” 字符串 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&amp;gt;&lt;/span&gt;1 + 1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 无法编译此属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 无法计算表达式“&quot;1 + 1&quot;.Length + 1”。未找到方法“System.String.Length + 1” --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv2&amp;gt;&lt;/span&gt;$(Walterlv.Length + 1)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv2&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个属性将得到一个 “5 + 1” 字符串 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv3&amp;gt;&lt;/span&gt;$(Walterlv.Length) + 1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Walterlv3&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions#BKMK_PropertyFunctions&quot;&gt;Property Functions - Visual Studio 2015 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 11 May 2019 06:53:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-numeric-methods.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-numeric-methods.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>使用 DISM 工具检查并修复 Windows 系统文件</title>
        <description>&lt;p&gt;DISM，Deployment Image Servicing and Management，部署映像服务和管理。本文介绍使用此工具检查并修复 Windows 的系统文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系统要求&quot;&gt;系统要求&lt;/h2&gt;

&lt;p&gt;Windows 8/8.1 和 Windows 10 开始提供 DISM 工具。&lt;/p&gt;

&lt;p&gt;相比于我在另一篇博客中提及的 sfc，DISM 利用 Windows 系统镜像来完成修复，所以更容易修复成功。关于 sfc（System File Check）可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/system-file-check-scan-and-repair-system-files&quot;&gt;使用 System File Check (SFC) 工具检查并修复 Windows 系统文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用方法&quot;&gt;使用方法&lt;/h2&gt;

&lt;p&gt;使用管理员权限启动 CMD，然后输入命令：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;DISM.exe /Online /Cleanup-image /Restorehealth
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;运行后等待其运行完成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-09-21-06-09.png&quot; alt=&quot;DISM 修复系统的命令&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用本地镜像&quot;&gt;使用本地镜像&lt;/h2&gt;

&lt;p&gt;上面的命令依赖于 Windows Update 服务来获取在线的镜像进行恢复。如果 Windows Update 服务已经挂了，那么这个命令是无法正常完成的。&lt;/p&gt;

&lt;p&gt;这时需要额外添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;/Source:&lt;/code&gt; 来指定修复所使用的本地文件：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;DISM.exe /Online /Cleanup-Image /RestoreHealth /Source:C:\RepairSource\Windows /LimitAccess
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\RepairSource\Windows&lt;/code&gt; 需要换成自己的本地镜像路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.thewindowsclub.com/fix-windows-update-using-dism&quot;&gt;Fix corrupted Windows Update system files using DISM Tool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.windowscentral.com/how-use-dism-command-line-utility-repair-windows-10-image&quot;&gt;How to use DISM command-line utility to repair a Windows 10 image - Windows Central&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/en-us/help/947821/fix-windows-update-errors-by-using-the-dism-or-system-update-readiness&quot;&gt;Fix Windows Update errors by using the DISM or System Update Readiness tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 10 May 2019 01:02:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dism-restore-health.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dism-restore-health.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>使用 System File Check (SFC) 工具检查并修复 Windows 系统文件</title>
        <description>&lt;p&gt;sfc.exe 这个程序的名称指的是 System File Check，用于做系统文件检查。本文介绍使用此命令检查并修复 Windows 系统文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系统要求&quot;&gt;系统要求&lt;/h2&gt;

&lt;p&gt;Windows Vista 及以上的操作系统才具有 sfc.exe 工具。 &lt;em&gt;相比于 Windows 7 开始提供 dism 工具。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;当然，虽然系统要求如此，但如果你使用的是 Windows 8/8.1 或者 Windows 10，那么便建议使用 DISM。可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dism-restore-health&quot;&gt;使用 DISM 工具检查并修复 Windows 系统文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用方法&quot;&gt;使用方法&lt;/h2&gt;

&lt;p&gt;使用管理员权限启动 CMD，然后输入命令：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;sfc /scannow
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来等待命令执行完成即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-09-18-44-35.png&quot; alt=&quot;sfc /scannow&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;命令结果&quot;&gt;命令结果&lt;/h2&gt;

&lt;p&gt;如果以上命令可以正常完成，那么你可能会遇到三种不同的提示（以下为中英双语版本）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Windows Resource Protection did not find any integrity violations.
    &lt;ul&gt;
      &lt;li&gt;Windows 资源保护找不到任何完整性冲突。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows Resource Protection could not perform the requested operation.
    &lt;ul&gt;
      &lt;li&gt;Windows 资源保护无法执行请求的操作。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows Resource Protection found corrupt files and successfully repaired them. Details are included in the CBS.Log %WinDir%\Logs\CBS\CBS.log.
    &lt;ul&gt;
      &lt;li&gt;Windows 资源保护找到了损坏的文件并已成功将其修复。 详细信息包含在 CBS.Log（路径为 %WinDir%\Logs\CBS\CBS.log）中。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows Resource Protection found corrupt files but was unable to fix some of them. Details are included in the CBS.Log %WinDir%\Logs\CBS\CBS.log.
    &lt;ul&gt;
      &lt;li&gt;Windows 资源保护找到了损坏的文件但无法修复其中的某些文件。 详细信息包含在 CBS.Log（路径为 %WinDir%\Logs\CBS\CBS.log）中。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;出现第一种提示，则说明没有任何丢失或损坏的系统文件。如果系统存在其他问题，则需要找其他方法来修复。&lt;/p&gt;

&lt;p&gt;出现第二种提示，你需要确保 %WinDir%\WinSxS\Temp 下存在 PendingDeletes 和 PendingRenames 文件夹；然后去安全模式中重新尝试此命令。&lt;/p&gt;

&lt;p&gt;出现第三种提示，则已经修复了损坏的文件。&lt;/p&gt;

&lt;p&gt;而出现第四种提示的话，你可以多次尝试执行此命令。可能多次执行后逐渐修复了所有的文件，也可能毫无作用。这个时候需要考虑其他的方法来修复系统了。&lt;/p&gt;

&lt;h2 id=&quot;此工具的其他命令&quot;&gt;此工具的其他命令&lt;/h2&gt;

&lt;p&gt;可以只做检查而不用尝试修复。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;sfc /verifyonly
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/en-us/windows/forum/windows_10-update/system-file-check-sfc-scan-and-repair-system-files/bc609315-da1f-4775-812c-695b60477a93?auth=1&quot;&gt;System file check (SFC) Scan and Repair System Files - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/en-us/help/929833/use-the-system-file-checker-tool-to-repair-missing-or-corrupted-system&quot;&gt;Use the System File Checker tool to repair missing or corrupted system files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 09 May 2019 13:02:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/system-file-check-scan-and-repair-system-files.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/system-file-check-scan-and-repair-system-files.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Visual Studio 使用 Parallel Builds Monitor 插件迅速找出编译速度慢的瓶颈，优化编译速度</title>
        <description>&lt;p&gt;嫌项目编译太慢？不一定是 Visual Studio 的问题，有可能是你项目的引用关系决定这个编译时间真的省不下来。&lt;/p&gt;

&lt;p&gt;可是，编译瓶颈在哪里呢？本文介绍 Parallel Builds Monitor 插件，帮助你迅速找出编译瓶颈。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载安装-parallel-builds-monitor&quot;&gt;下载安装 Parallel Builds Monitor&lt;/h2&gt;

&lt;p&gt;前往 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ivson4.ParallelBuildsMonitor-18691&quot;&gt;Parallel Builds Monitor - Visual Studio Marketplace&lt;/a&gt; 下载插件安装。&lt;/p&gt;

&lt;p&gt;之后启动 Visual Studio 2019，你就能在 “其他窗口” 中找到 “Parallel Builds Monitor” 窗口了。请点击打开它。&lt;/p&gt;

&lt;h2 id=&quot;编译项目&quot;&gt;编译项目&lt;/h2&gt;

&lt;p&gt;现在，使用 Visual Studio 编译一个项目，点开这个窗口，一个正在进行中的甘特图将呈现出来：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-05-05-21-34-42.png&quot; alt=&quot;并行编译窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;寻找瓶颈&quot;&gt;寻找瓶颈&lt;/h2&gt;

&lt;p&gt;我们可以通过此插件寻找到多种可能的瓶颈：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;项目依赖瓶颈&lt;/li&gt;
  &lt;li&gt;CPU 瓶颈&lt;/li&gt;
  &lt;li&gt;IO 瓶颈&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;项目依赖瓶颈&quot;&gt;项目依赖瓶颈&lt;/h3&gt;

&lt;p&gt;看上面的那张图，这里存在典型的项目依赖瓶颈。因为在编译的中后期，几个编译时间最长的项目，其编译过程完全是串联起来编译的。&lt;/p&gt;

&lt;p&gt;这里串联起来的每一个项目，都是依赖于前一个项目的。所以要解决掉这部分的性能瓶颈，我们需要断开这几个项目之间的依赖关系，这样它们能变成并行的编译。&lt;/p&gt;

&lt;h3 id=&quot;cpu-瓶颈&quot;&gt;CPU 瓶颈&lt;/h3&gt;

&lt;p&gt;通常，CPU 成为瓶颈在编译中是个好事情，这意味着无关不必要的编译过程非常少，主要耗时都在编译代码的部分。当然，如果你有一些自定义的编译过程浪费了 CPU 占用那是另外一回事。&lt;/p&gt;

&lt;p&gt;比如我之前写过自己可以做一个工具包，在编译期间会执行一些代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;io-瓶颈&quot;&gt;IO 瓶颈&lt;/h3&gt;

&lt;p&gt;IO 本不应该成为瓶颈。如果你的项目就是存在非常多的依赖文件需要拷贝，那么应该尽可能利用差量编译来避免重复拷贝文件。&lt;/p&gt;

&lt;!-- ## 解决瓶颈

在上面的图片中，我们首先要解决的瓶颈就是项目依赖瓶颈，因为从图中我们可以得出，如果后面的 3~4 个项目可以并行编译，那么将节省 15~20 秒甚至更多的编译时间。做法，就是删除项目依赖，将无法编译过的代码采用依赖注入等方式解耦。 --&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ivson4.ParallelBuildsMonitor-18691&quot;&gt;Parallel Builds Monitor - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/KrzysztofBuchacz/ParallelBuildsMonitor&quot;&gt;KrzysztofBuchacz/ParallelBuildsMonitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 05 May 2019 13:42:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/visual-studio-extension-parallel-builds-monitor.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/visual-studio-extension-parallel-builds-monitor.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 EnumWindows 找到满足你要求的窗口</title>
        <description>&lt;p&gt;在 Windows 应用开发中，如果需要操作其他的窗口，那么可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt; 这个 API 来枚举这些窗口。&lt;/p&gt;

&lt;p&gt;本文介绍使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt; 来枚举并找到自己关心的窗口（如 QQ/TIM 窗口）。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;enumwindows&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;你可以在微软官网了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;要在 C# 代码中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumWindows&lt;/code&gt;，你需要编写平台调用 P/Invoke 代码。使用我在另一篇博客中的方法可以自动生成这样的平台调用代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/pinvoke-net-visual-studio-extension&quot;&gt;使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我这里直接贴出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndEnumProc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpEnumFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;遍历所有的顶层窗口&quot;&gt;遍历所有的顶层窗口&lt;/h2&gt;

&lt;p&gt;官方文档对此 API 的描述是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;遍历屏幕上所有的顶层窗口，然后给回调函数传入每个遍历窗口的句柄。&lt;/p&gt;

&lt;p&gt;不过，并不是所有遍历的窗口都是顶层窗口，有一些非顶级系统窗口也会遍历到，详见：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-enumwindows#remarks&quot;&gt;EnumWindows 中的备注节&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;所以，如果需要遍历得到所有窗口的集合，那么可以使用如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 可自行加入一些过滤条件。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;遍历具有指定类名或者标题的窗口&quot;&gt;遍历具有指定类名或者标题的窗口&lt;/h2&gt;

&lt;p&gt;我们需要添加一些可以用于过滤窗口的 Win32 API。以下是我们即将用到的两个：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 获取窗口的类名。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 获取窗口的标题。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nMaxCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是根据类名找到窗口的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindowByClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;EnumWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnWindowEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;GetClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;windowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用此方法，我们可以传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;txguifoundation&quot;&lt;/code&gt; 找到 QQ/TIM 的窗口：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qqHwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindowByClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;txguifoundation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;要获取窗口的标题，或者把标题作为过滤条件，则使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetWindowText&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在 QQ/TIM 中，窗口的标题是聊天对方的名字或者群聊名称。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;GetWindowText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lptrString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-enumwindows&quot;&gt;EnumWindows function (winuser.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getclassname&quot;&gt;GetClassName function (winuser.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 30 Apr 2019 13:11:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/find-specific-window-by-enum-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/find-specific-window-by-enum-windows.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>安装和运行 .NET Core 版本的 PowerShell</title>
        <description>&lt;p&gt;Windows 自带一个强大的 PowerShell，不过自带的 PowerShell 一直是基于 .NET Framework 的版本。你可以下载安装一个 .NET Core 版本的 PowerShell，以便获得 .NET Core 的各种好处。包括跨平台，以及更好的性能。&lt;/p&gt;

&lt;p&gt;本文将介绍在你的 Windows 系统上安装一个 .NET Core 版本的 PowerShell。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-16-13-46.png&quot; alt=&quot;PowerShell Core 的图标&quot; /&gt;&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载和安装&quot;&gt;下载和安装&lt;/h2&gt;

&lt;p&gt;前往 .NET Core 版本 PowerShell 的发布页面来下载 PowerShell 全平台的安装包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PowerShell/PowerShell/releases&quot;&gt;Releases · PowerShell/PowerShell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Windows 平台上建议下载 msi 格式的安装包，这样它可以帮助你完成大多数的安装任务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-16-05-32.png&quot; alt=&quot;PowerShell 安装界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-19-37-42.png&quot; alt=&quot;PowerShell 安装配置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;运行&quot;&gt;运行&lt;/h2&gt;

&lt;p&gt;在安装完成之后启动新的 .NET Core 版本的 PowerShell 可以看见新的 PowerShell。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-16-08-33.png&quot; alt=&quot;.NET Core 版本的 PowerShell&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在任何一个文件夹中右键可打开 PowerShell 或者以管理员权限打开 PowerShell。这与自带的 PowerShell 的玩法是类似的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-16-14-49.png&quot; alt=&quot;使用右键菜单打开 PowerShell&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;在其他终端使用-powershell-core&quot;&gt;在其他终端使用 PowerShell Core&lt;/h2&gt;

&lt;p&gt;如果你要在其他的终端使用 PowerShell Core，直接输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh&lt;/code&gt; 即可。其原理可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/run-your-application-without-full-executable-path&quot;&gt;让你的 Windows 应用程序在任意路径也能够直接通过文件名执行 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-17-28-53.png&quot; alt=&quot;在 cmd 中启动 PowerShell Core&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Apr 2019 11:37:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/install-and-run-powershell-core.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/install-and-run-powershell-core.html</guid>
        
        
        <category>dotnet</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>XAML 很少人知道的科技</title>
        <description>&lt;p&gt;本文介绍不那么常见的 XAML 相关的知识。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;thickness-可以用空格分隔&quot;&gt;Thickness 可以用空格分隔&lt;/h2&gt;

&lt;p&gt;当你用设计器修改元素的 Margin 时，你会看到用逗号分隔的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thickness&lt;/code&gt; 属性。使用设计器或者属性面板时，使用逗号是默认的行为。&lt;/p&gt;

&lt;p&gt;不过你有试过，使用空格分隔吗？&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10 12 0 0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用逗号设置多值枚举&quot;&gt;使用逗号（&lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt;）设置多值枚举&lt;/h2&gt;

&lt;p&gt;有一些枚举标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Flags]&lt;/code&gt; 特性，这样的枚举可以通过位运算设置多个值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NonClientFrameEdges&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略枚举内的值。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么在 XAML 里面如何设置多个枚举值呢？使用逗号（&lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt;）即可，如下面的例子：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NonClientFrameEdges=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left,Bottom,Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 64 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;UseAeroCaptionButtons=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用加号设置多值枚举&quot;&gt;使用加号（&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;）设置多值枚举&lt;/h2&gt;

&lt;p&gt;使用逗号（&lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt;） 设置多值枚举是通用的写法，但是在 WPF/UWP 中设置按键/键盘快捷键的时候又有加号（&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;）的写法。如下面的例子：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;KeyBinding&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Static WalterlvCommands.Foo}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Modifiers=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Control+Shift&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;W&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Modifiers&lt;/code&gt; 属性的类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ModifierKeys&lt;/code&gt;，实际上是因为这个类型特殊地编写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt; 来转换字符串，所以键盘快捷键多值枚举使用的位或运算用的是加号（&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;）。&lt;/p&gt;

&lt;h2 id=&quot;设置-url-型的-xaml-命名空间xmlns&quot;&gt;设置 Url 型的 XAML 命名空间（xmlns）&lt;/h2&gt;

&lt;p&gt;WPF/UWP 中原生控件的 XAML 命名空间是 &lt;a href=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&gt;http://schemas.microsoft.com/winfx/2006/xaml/presentation&lt;/a&gt;，与 XAML 编译器相关的 XAML 命名空间是 &lt;a href=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&gt;http://schemas.microsoft.com/winfx/2006/xaml&lt;/a&gt;，还有其他 Url 形式的 XAML 命名空间。&lt;/p&gt;

&lt;p&gt;只需要在库中写如下特性（Attribute）即可将命名空间指定为一个 url：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://walterlv.github.io/demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NewCsprojDemo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详情请阅读博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/define-xmlns-of-for-xaml&quot;&gt;让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间（xmlns）和命名空间前缀&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此写法要生效，定义的组件与使用的组件不能在同一程序集。&lt;/p&gt;

&lt;h2 id=&quot;设置默认的-xaml-命名空间前缀&quot;&gt;设置默认的 XAML 命名空间前缀&lt;/h2&gt;

&lt;p&gt;WPF/UWP XAML 编译器的命名空间前缀是 &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;。如果你写了自己的控件，希望给控件指定一个默认的命名空间前缀，那么可以通过在库中写如下特性（Attribute）实现：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://walterlv.github.io/demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，当 XAML 设计器帮助你自动添加命名空间时，将会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;w&lt;/code&gt; 前缀。虽然实际上你也能随便改。&lt;/p&gt;

&lt;p&gt;详情请阅读博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/define-xmlns-of-for-xaml&quot;&gt;让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间（xmlns）和命名空间前缀&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此写法要生效，定义的组件与使用的组件不能在同一程序集。&lt;/p&gt;

&lt;h2 id=&quot;让你做的控件库不需要-xaml-命名空间前缀&quot;&gt;让你做的控件库不需要 XAML 命名空间前缀&lt;/h2&gt;

&lt;p&gt;自己写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DemoPage&lt;/code&gt;，要在 XAML 中使用，一般需要添加命名空间前缀才可以。但是也可以不写：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HuyaHearhira.UserControl1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoPage&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;方法是在库中定义命名空间前缀为 &lt;a href=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&gt;http://schemas.microsoft.com/winfx/2006/xaml/presentation&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NewCsprojDemo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此写法要生效，定义的组件与使用的组件不能在同一程序集。&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Apr 2019 11:08:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/those-people-dont-know-about-xaml.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/those-people-dont-know-about-xaml.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>WPF 程序鼠标在窗口之外的时候，控件拿到的鼠标位置在哪里？</title>
        <description>&lt;p&gt;在 WPF 程序中，我们有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.GetPosition(IInputElement relativeTo)&lt;/code&gt; 方法可以拿到鼠标当前相对于某个 WPF 控件的位置，也可以通过在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseMove&lt;/code&gt; 事件中通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.GetPosition(IInputElement relativeTo)&lt;/code&gt; 方法拿到同样的信息。不过，在任意时刻去获取鼠标位置的时候，如果鼠标在窗口之外，将获取到什么点呢？&lt;/p&gt;

&lt;p&gt;本文将介绍鼠标在窗口之外时获取到的鼠标位置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;可用于演示的-demo&quot;&gt;可用于演示的 DEMO&lt;/h2&gt;

&lt;p&gt;直接使用 Visual Studio 2019 创建一个空的 WPF 应用程序。默认 .NET Core 版本的 WPF 会带一个文本框和一个按钮。我们现在就用这两个按钮来显示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.GetPosition&lt;/code&gt; 获取到的值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rendering&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DebugTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebugTextBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DebugButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebugButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;观察现象&quot;&gt;观察现象&lt;/h2&gt;

&lt;p&gt;我们运行这个最简单的 Demo，然后不断移动鼠标，可以观察到一旦鼠标脱离窗口客户区，获取到的坐标点将完全固定。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-mouse-get-position.gif&quot; alt=&quot;鼠标在各处时获取到的点坐标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果不知道客户区是什么，可以阅读下面我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-style-using-window-chrome&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在以上图中，我拖动改变了窗口的位置，这时将鼠标移动至离开客户区后，获取到的坐标点又被固定为另一个数值。&lt;/p&gt;

&lt;h2 id=&quot;推断结论&quot;&gt;推断结论&lt;/h2&gt;

&lt;p&gt;从上面的动图中以及我实际的测量发现，当鼠标移出窗口的客户区之后，获取鼠标的坐标的时候始终拿到的是屏幕的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(0, 0)&lt;/code&gt; 点。如果有多个屏幕，是所有屏幕组合起来的虚拟屏幕的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(0, 0)&lt;/code&gt; 点。&lt;/p&gt;

&lt;p&gt;验证这一点，我们把窗口移动到屏幕的左上角后，将鼠标移出客户区，左上角的控件其获取到的鼠标位置已经变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;(0, 31)&lt;/code&gt;，而这个是窗口标题栏非客户区的高度。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-14-41-36.png&quot; alt=&quot;将窗口移至屏幕的左上角&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;原理&quot;&gt;原理&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.GetPosition&lt;/code&gt; 获取鼠标相对于控件的坐标点的方法在内部的最终实现是 user32.dll 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClientToScreen&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此方法需要使用到一个窗口句柄参数，此参数的含义：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A handle to the window whose client area is used for the conversion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;用于转换坐标点的窗口句柄，坐标会被转换到窗口的客户区部分。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the function succeeds, the return value is nonzero.&lt;br /&gt;
If the function fails, the return value is zero.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果此方法成功，将返回非零的坐标值；如果失败，将返回 0。&lt;/p&gt;

&lt;p&gt;而鼠标在窗口客户区之外的时候，此方法将返回 0，并且经过后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToPoint()&lt;/code&gt; 方法转换到控件的坐标下。于是这才得到了我们刚刚观察到的坐标值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityTreatAsSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// For now we only know how to use HwndSource.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentationSource&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HandleRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CriticalHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClient&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AdjustForRightToLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClientToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptClientRTLAdjusted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/4232281/6233938&quot;&gt;How do I get the current mouse screen coordinates in WPF? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.pinvoke.net/default.aspx/user32.clienttoscreen&quot;&gt;pinvoke.net: clienttoscreen (user32)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/34534279/6233938&quot;&gt;c# - ClientToScreen unexpected return values? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-clienttoscreen&quot;&gt;ClientToScreen function (winuser.h) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 30 Apr 2019 06:53:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-mouse-position-when-mouse-is-out-of-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-mouse-position-when-mouse-is-out-of-window.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何修改 Visual Studio 新建项目时的默认路径</title>
        <description>&lt;p&gt;Visual Studio 创建新项目的时候，默认位置在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\source\repos\&lt;/code&gt; 下。多数时候，我们都希望将其改为一个更适合自己开发习惯的路径。实际上修改默认路径并不是一个麻烦的事情，但是当紧急需要修改的时候，你可能找不到设置项在哪里。&lt;/p&gt;

&lt;p&gt;本文介绍如何修改这个默认路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;默认位置&quot;&gt;默认位置&lt;/h2&gt;

&lt;p&gt;默认位置在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\source\repos\&lt;/code&gt; 下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-10-17-14.png&quot; alt=&quot;默认位置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;visual-studio-的设置项&quot;&gt;Visual Studio 的设置项&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 中打开菜单 “工具” -&amp;gt; “选项”；然后找到 “项目和解决方案” -&amp;gt; “位置” 标签。“项目位置” 一栏就是设置新建项目默认路径的地方。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-10-24-22.png&quot; alt=&quot;中文版的设置界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果是英文本，则打开菜单 “Tools” -&amp;gt; “Options”；然后找到 “Projects and Solutions” -&amp;gt; “Locations” 标签。“Projects location” 一栏就是设置新建项目默认路径的地方。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-10-27-35.png&quot; alt=&quot;英文版的设置界面&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;修改后的默认位置&quot;&gt;修改后的默认位置&lt;/h2&gt;

&lt;p&gt;修改完后，再次新建项目，就可以看到修改后的默认路径了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-30-10-28-56.png&quot; alt=&quot;修改后的默认位置&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Apr 2019 02:29:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/change-visual-studio-default-project-location.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/change-visual-studio-default-project-location.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>使用 dotnet 命令行配合 vscode 完成一个完整 .NET 解决方案的编写和调试</title>
        <description>&lt;p&gt;如果你是开发个人项目，那就直接用 Visual Studio Community 版本吧，对个人免费，对小团体免费，不需要这么折腾。&lt;/p&gt;

&lt;p&gt;如果你是 Mac / Linux 用户，不想用 Visual Studio for Mac 版；或者不想用 Visual Studio for Windows 版那么重磅的 IDE 来开发简单的 .NET Core 程序；或者你就是想像我这么折腾，那我们就开始吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装必要的软件和插件&quot;&gt;安装必要的软件和插件&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;点击这里下载正式或者预览版的 .NET Core&lt;/a&gt; 然后安装&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/download&quot;&gt;点击这里下载 Visual Studio Code&lt;/a&gt; 然后安装&lt;/li&gt;
  &lt;li&gt;在 Visual Studio Code 里安装 C# for Visual Studio Code 插件（步骤如下图所示）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-01-52.png&quot; alt=&quot;安装 C# for Visual Studio Code 插件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;搜索的时候，推荐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;OmniSharp&lt;/code&gt; 关键字，因为这可以得到唯一的结果，你不会弄混淆。&lt;em&gt;如果你使用 C# 作为关键字，那需要小心，你得找到名字只有 C#，点开之后是 C# for Visual Studio Code 的那款插件。因为可能装错，所以我不推荐这么做。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;对于新版的 Visual Studio Code，装完会自动启用，所以你不用担心。我们可以后续步骤了。&lt;/p&gt;

&lt;h2 id=&quot;创建一个-net-core-控制台项目&quot;&gt;创建一个 .NET Core 控制台项目&lt;/h2&gt;

&lt;p&gt;准备一个空的文件夹，这个文件夹将会成为我们解决方案所在的文件夹，也就是 sln 文件所在的文件夹。在这个空的文件夹中打开 VSCode，然后打开 VSCode 的终端。&lt;/p&gt;

&lt;p&gt;在 VSCode 中的终端中输入：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样会在当前的文件夹中创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 的子文件夹，并且在此文件夹中新建一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 的控制台项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-09-53-28.png&quot; alt=&quot;创建一个控制台项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你观察我们刚刚创建的项目，你会发现里面有一个 csproj 文件和一个 Program.cs 文件。csproj 文件是 Sdk 风格的项目文件，而 Program.cs 里面包含最简单的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Hello World&lt;/code&gt; 代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们会考虑在一个子文件夹中创建项目，是因为我们会一步步创建一个比较复杂的解决方案，用以演示比较完整的使用 VSCode 开发 .NET 程序的过程。&lt;/p&gt;

&lt;h2 id=&quot;添加一个解决方案&quot;&gt;添加一个解决方案&lt;/h2&gt;

&lt;p&gt;我们现在创建一个在 Visual Studio 会特别熟悉的解决方案，sln 文件。&lt;/p&gt;

&lt;p&gt;使用以下命令创建一个解决方案文件：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sln&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，这个解决方案文件还是空的，不包含任何项目，于是我们把我们一开始创建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 项目加入到此 sln 文件中。&lt;/p&gt;

&lt;p&gt;使用以下命令添加：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sln&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Demo\Walterlv.Demo.csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，我们的解决方案中，就存在一个可以运行的控制台项目了。&lt;/p&gt;

&lt;h2 id=&quot;开始调试最简单的程序&quot;&gt;开始调试最简单的程序&lt;/h2&gt;

&lt;p&gt;理论上，你按下 F5，选择 .NET Core 后就能自动生成调试所需的 launch.json 和 tasks.json 文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/equip-vscode-for-dotnet-core-app-debugging&quot;&gt;让你的 VSCode 具备调试 C# 语言 .NET Core 程序的能力&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果不能生成所需的文件，你可以使用以下博客中的方法，手动添加这两个文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/equip-vscode-manually-for-dotnet-core-app-debugging&quot;&gt;手工编辑 tasks.json 和 launch.json，让你的 VSCode 具备调试 .NET Core 程序的能力&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在经过以上两篇博客中的方法之后，你将可以跑起来你的程序。&lt;/p&gt;

&lt;p&gt;如果遇到了编译错误……呃这么简单的程序怎么可能遇到编译错误呢？一定是因为之前的操作有问题。可以考虑删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;obj&lt;/code&gt; 文件夹，然后输入以下命令自行编译：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个命令会还原 NuGet 包，然后使用 .NET Core 版本的 MSBuild 编译你的解决方案。在此之后，你并不需要总是输入此命令，只需要像 Visual Studio 一样按下 F5 即可调试。&lt;/p&gt;

&lt;h2 id=&quot;引用项目&quot;&gt;引用项目&lt;/h2&gt;

&lt;p&gt;现在我们演示如何引用项目。&lt;/p&gt;

&lt;p&gt;首先使用以下命令创建一个类库项目：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classlib&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Library&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将其添加到 sln 中。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sln&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Library\Walterlv.Library.csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们的目录结构现在是这样的（稍微改了一点代码）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-10-32-29.png&quot; alt=&quot;目录结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后让我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 项目引用这个刚刚创建的项目：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reference&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Library\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们即可在 Program.cs 中使用到刚刚 Class1.cs 中编写的方法（见上面截图中写的方法）。&lt;/p&gt;

&lt;p&gt;不过，当你写下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Class1&lt;/code&gt; 后，会没有此名称，但有快速操作提示可以自动添加命名空间（就像没有装 ReSharper 的 Visual Studio 的效果一样）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-10-39-02.png&quot; alt=&quot;有快速操作提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-10-40-14.png&quot; alt=&quot;可添加命名空间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-10-41-13.png&quot; alt=&quot;有智能感知提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时再按下 F5 运行，可以看到多输出了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv is a 逗比&lt;/code&gt; 这样的提示，我们成功使用到了刚刚引用的类。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-29-10-37-30.png&quot; alt=&quot;运行的结果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;引用-nuget-包&quot;&gt;引用 NuGet 包&lt;/h2&gt;

&lt;p&gt;接下来介绍如何引用 NuGet 包。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样可以给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 项目引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Newtonsoft.Json&lt;/code&gt; 包。&lt;/p&gt;

&lt;p&gt;接下来就像前面一节我们所描述的那样使用这个包里面的类就好了。&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Apr 2019 03:09:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/develop-an-app-using-vscode-and-dotnet-cli.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/develop-an-app-using-vscode-and-dotnet-cli.html</guid>
        
        
        <category>dotnet</category>
        
        <category>vscode</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Visual Studio 通过修改项目的调试配置文件做到临时调试的时候不要编译（解决大项目编译缓慢问题）</title>
        <description>&lt;p&gt;.NET 托管程序的编译速度比非托管程序要快非常多，即便是 .NET Core，只要不编译成 Native 程序，编译速度也是很快的。然而总是有一些逗比大项目编译速度非常缓慢（我指的是分钟级别的），而且还没做好差量编译；于是每一次编译都需要等待几十秒到数分钟。这显然是非常影响效率的。&lt;/p&gt;

&lt;p&gt;在解决完项目的编译速度问题之前，如何能够临时进行快速调试改错呢？本文将介绍在 Visual Studio 中不进行编译就调试的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我找到了两种临时调试而不用编译的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/debug-without-building-for-visual-studio-project&quot;&gt;在 Visual Studio 的设置界面设置启动前不编译&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/debug-project-without-building-via-launch-settings&quot;&gt;通过修改项目调试配置文件（本文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;新建一个普通的类库项目，右击项目，属性，打开属性设置页面。进入“调试”标签：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-25-19-29-31.png&quot; alt=&quot;调试标签&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，将默认的启动从“项目”改为“可执行文件”，然后将我们本来调试时输出的程序路径贴上去。&lt;/p&gt;

&lt;p&gt;现在，如果你不希望编译大项目而直接进行调试，那么将启动项目改为这个小项目即可。&lt;/p&gt;
</description>
        <pubDate>Fri, 26 Apr 2019 04:22:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/debug-project-without-building-via-launch-settings.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/debug-project-without-building-via-launch-settings.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Visual Studio 如何能够不进行编译就调试 .NET/C# 项目（用于解决大项目编译缓慢的问题）</title>
        <description>&lt;p&gt;.NET 托管程序的编译速度比非托管程序要快非常多，即便是 .NET Core，只要不编译成 Native 程序，编译速度也是很快的。然而总是有一些逗比大项目编译速度非常缓慢（我指的是分钟级别的），而且还没做好差量编译；于是每一次编译都需要等待几十秒到数分钟。这显然是非常影响效率的。&lt;/p&gt;

&lt;p&gt;在解决完项目的编译速度问题之前，如何能够临时进行快速调试改错呢？本文将介绍在 Visual Studio 中不进行编译就调试的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我找到了两种临时调试而不用编译的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/debug-without-building-for-visual-studio-project&quot;&gt;在 Visual Studio 的设置界面设置启动前不编译（本文）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/debug-project-without-building-via-launch-settings&quot;&gt;通过修改项目调试配置文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;不编译直接调试&quot;&gt;不编译直接调试&lt;/h2&gt;

&lt;p&gt;有时候只是为了定位 Bug 不断重复运行以调试程序，并没有修改代码。然而如果 Visual Studio 的差量编译因为逗比项目失效的话，就需要手动告诉 Visual Studio 不需要进行编译，直接进行调试。&lt;/p&gt;

&lt;h2 id=&quot;在-visual-studio-中设置编译选项&quot;&gt;在 Visual Studio 中设置编译选项&lt;/h2&gt;

&lt;p&gt;进入 &lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;项目和解决方案&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;生成并运行&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-26-20-08-51.png&quot; alt=&quot;打开选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-26-20-34-12.png&quot; alt=&quot;生成并运行&quot; /&gt;&lt;/p&gt;

&lt;p&gt;“当项目过期时”，选择“从不生成”。&lt;/p&gt;

&lt;p&gt;顺便附中文版截图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-26-20-39-51.png&quot; alt=&quot;中文版生成并运行&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，你再点击运行你的项目的时候，就不会再编译了，而是直接进入调试状态。&lt;/p&gt;

&lt;p&gt;这特别适合用来定位 Bug，因为这时基本不改什么代码，都是在尝试复现问题以及查看各种程序的中间状态。&lt;/p&gt;
</description>
        <pubDate>Fri, 26 Apr 2019 00:11:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/debug-without-building-for-visual-studio-project.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/debug-without-building-for-visual-studio-project.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>WPF 获取元素（Visual）相对于屏幕设备的缩放比例，可用于清晰显示图片</title>
        <description>&lt;p&gt;我们知道，在 WPF 中的坐标单位不是屏幕像素单位，所以如果需要知道某个控件的像素尺寸，以便做一些与屏幕像素尺寸相关的操作，就需要经过一些计算（例如得到屏幕的 DPI）。&lt;/p&gt;

&lt;p&gt;更繁琐的是，我们的控件可能外面有一些其他的控件做了 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransform&lt;/code&gt; 进行了一些缩放，于是了解到屏幕像素单位就更不容易了。&lt;/p&gt;

&lt;p&gt;本文将提供一套计算方法，帮助计算某个 WPF 控件相比于屏幕像素尺寸的缩放比例，用于进行屏幕像素级别的渲染控制。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个-wpf-控件会经历哪些缩放&quot;&gt;一个 WPF 控件会经历哪些缩放？&lt;/h2&gt;

&lt;p&gt;如下图，我画了一个屏幕，屏幕里面有一个 WPF 窗口，WPF 窗口里面有一个或者多个 ViewBox 或者设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransform&lt;/code&gt; 这样的缩放的控件，一层层嵌套下有我们的最终控件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-25-17-03-18.png&quot; alt=&quot;这些缩放&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，我们的控件如何得知此时相比于屏幕像素的缩放比呢？换句话说，如何得知此时此控件的显示占了多少个屏幕像素的宽高呢？&lt;/p&gt;

&lt;h2 id=&quot;分别计算所有的缩放&quot;&gt;分别计算所有的缩放&lt;/h2&gt;

&lt;p&gt;从上面的图中，我们可以得知，有两种不同种类的缩放：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;屏幕到 WPF 窗口的缩放&lt;/li&gt;
  &lt;li&gt;WPF 窗口内部的缩放&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;屏幕到-wpf-窗口的缩放&quot;&gt;屏幕到 WPF 窗口的缩放&lt;/h3&gt;

&lt;p&gt;我们知道 WPF 的单位叫做 DIP 设备无关单位。不过，我更希望引入 UWP 中的有效像素单位。实际上 WPF 和 UWP 的像素单位含义是一样的，只是 WPF 使用了一个画饼式的叫法，而 UWP 中的叫法就显得现实得多。&lt;/p&gt;

&lt;p&gt;你可以阅读我的另一篇博客了解到有效像素单位：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-uwp-effective-pixels-into-wpf&quot;&gt;将 UWP 的有效像素（Effective Pixels）引入 WPF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有效像素主要就是考虑了 DPI 缩放。于是实际上我们就是在计算 DPI 缩放。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// visual 是我们准备找到缩放量的控件。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TransformToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，我们使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource.FromVisual(visual)?.CompositionTarget&lt;/code&gt; 因为不同屏幕可能存在不同的 DPI。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development-for-wpf&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;wpf-窗口内部的缩放&quot;&gt;WPF 窗口内部的缩放&lt;/h3&gt;

&lt;p&gt;WPF 窗口内部的缩放，肯定不会是一层层自己去叠加。&lt;/p&gt;

&lt;p&gt;实际上 WPF 提供了方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;TransformToAncestor&lt;/code&gt; 可以计算一个两个具有父子关系的控件的相对变换量。&lt;/p&gt;

&lt;p&gt;于是我们需要找到 WPF 窗口中的根元素，可以通过不断查找可视化树的父级来找到根。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// VisualRoot 方法用于查找 visual 当前的可视化树的根，如果 visual 已经显示，则根会是窗口中的根元素。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MatrixTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TransformToAncestor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;我封装的源码&quot;&gt;我封装的源码&lt;/h2&gt;

&lt;p&gt;为了方便使用，我进行了一些封装。&lt;/p&gt;

&lt;p&gt;要获取某个 Visual 相比于屏幕的缩放量，则调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetScalingRatioToDevice&lt;/code&gt; 方法即可。&lt;/p&gt;

&lt;p&gt;代码已经上传至 gits：&lt;a href=&quot;https://gist.github.com/walterlv/6015ea19c9338b9e45ca053b102cf456&quot;&gt;https://gist.github.com/walterlv/6015ea19c9338b9e45ca053b102cf456&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VisualScalingExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个 &amp;lt;paramref name=&quot;visual&quot;/&amp;gt; 在显示设备上的尺寸相对于自身尺寸的缩放比。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetScalingRatioToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTransformInfoToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个 &amp;lt;paramref name=&quot;visual&quot;/&amp;gt; 在显示设备上的尺寸相对于自身尺寸的缩放比和旋转角度（顺时针为正角度）。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTransformInfoToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 计算此 Visual 在 WPF 窗口内部的缩放（含 ScaleTransform 等）。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MatrixTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TransformToAncestor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 计算此 WPF 窗口相比于设备的外部缩放（含 DPI 缩放等）。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TransformToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果元素有旋转，则计算旋转分量。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitVector&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitVector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AngleBetween&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitVector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 计算考虑了旋转的综合缩放比。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 寻找一个 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt; 连接着的视觉树的根。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 通常，如果这个 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt; 显示在窗口中，则根为 &amp;lt;see cref=&quot;Window&quot;/&amp;gt;；&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 不过，如果此 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt; 没有显示出来，则根为某一个包含它的 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt;。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 如果此 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt; 未连接到任何其它 &amp;lt;see cref=&quot;Visual&quot;/&amp;gt;，则根为它自身。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 25 Apr 2019 09:24:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-wpf-visual-scaling-ratio-to-device.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-wpf-visual-scaling-ratio-to-device.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>MSBuild 中的特殊字符（$ @ % 等）：含义、用法以及转义</title>
        <description>&lt;p&gt;在 MSBuild 中有一些特殊字符，如 &lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt; 等，本文介绍他们的含义，如何使用他们，以及你真的需要这些字符的时候如何编写他们。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;特殊字符&quot;&gt;特殊字符&lt;/h2&gt;

&lt;p&gt;MSBuild 中有这些特殊字符：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;含义和用法&quot;&gt;含义和用法&lt;/h2&gt;

&lt;h3&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;引用一个属性或者环境变量。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\$(Configuration)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如以下两篇博客列出了一些最典型的使用场景。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-properties-that-affetcs-project-output-path&quot;&gt;如何更精准地设置 C# / .NET Core 项目的输出路径？（包括添加和删除各种前后缀）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/environment-variables-in-csproj&quot;&gt;在 csproj 文件中使用系统环境变量的值（示例将 dll 生成到 AppData 目录下）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-1&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;引用一个集合。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;References:&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Reference)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如以下两篇博客列出了一些最典型的使用场景：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/resolve-project-references-using-target&quot;&gt;在 Target 中获取项目引用的所有依赖（dll/NuGet/Project）的路径&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/include-dependencies-into-nuget-tool-package&quot;&gt;在制作跨平台的 NuGet 工具包时，如何将工具（exe/dll）的所有依赖一并放入包中&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-2&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;引用集合中某一个项的某个属性。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xxx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Compile)=%(Compile.CopyToOutputDirectory)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Warning&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Walterlv)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如下面两篇博客列出了此字符的一些使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-referencing-metadata&quot;&gt;在项目文件 csproj 中或者 MSBuild 的 Target 中使用 % 引用集合中每一项的属性&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-3&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在形成一个字符串的时候，会使用到此字符。&lt;/p&gt;

&lt;p&gt;下面这篇博客列出了此字符的一些使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-write-msbuild-conditions&quot;&gt;MSBuild 如何编写带条件的属性、集合和任务 Condition？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-4&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果存在分号，那么在形成一个集合的时候，会被识别为集合中的各个项之间的分隔符。&lt;/p&gt;

&lt;p&gt;有时候你真的需要分号而不是作为分隔符的时候，需要进行转义：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-how-to-use-writelinestofile-to-write-the-semicolons-to-file&quot;&gt;Roslyn how to use WriteLinesToFile to write the semicolons to file - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-和-&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;作为通配符使用。一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 表示文件或者文件夹通配符，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;**&lt;/code&gt; 则表示任意层级的文件或文件夹。&lt;/p&gt;

&lt;p&gt;下面这篇博客虽然古老，却也说明了其用法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/vs/2017/09/26/wildcards-in-vs-projects.html&quot;&gt;为 Visual Studio 使用通配符批量添加项目文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;转义&quot;&gt;转义&lt;/h2&gt;

&lt;p&gt;在 MSBuild 中，由于这些特殊字符其实非常常见，所以与一些已有的值很容易冲突，所以需要转义。&lt;/p&gt;

&lt;p&gt;转义可以使用 ASCII 编码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%24&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%40&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%25&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%27&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%3B&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%3F&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;%2A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;转义方法一：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv1%3BWalterlv2.cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样得到的将是一个名字为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv1;Walterlv2.cs&lt;/code&gt; 的文件，而不是两个文件。&lt;/p&gt;

&lt;p&gt;转义方法二：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$([MSBuild]::Escape('Walterlv1;Walterlv2.cs'))&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详细方法可参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/roslyn-how-to-use-writelinestofile-to-write-the-semicolons-to-file&quot;&gt;Roslyn how to use WriteLinesToFile to write the semicolons to file - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-special-characters?view=vs-2019&quot;&gt;MSBuild Special Characters - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-escape-special-characters-in-msbuild?view=vs-2019&quot;&gt;How to: Escape Special Characters in MSBuild - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 24 Apr 2019 12:48:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-special-characters.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-special-characters.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在项目文件 csproj 中或者 MSBuild 的 Target 中使用 % 引用集合中每一项的属性</title>
        <description>&lt;p&gt;在编写项目文件或者 MSBuild Target 文件的时候，我们经常会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Foo Include=&quot;Identity&quot; /&amp;gt;&lt;/code&gt; 来定义集合中的一项。在定义的同时，我们也会额外指定一些属性。&lt;/p&gt;

&lt;p&gt;然而这些属性如何拿到并且使用呢？本文将介绍使用方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;将下面的代码放到你项目文件的末尾，最后一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/code&gt; 的前面，可以在编译的时候看到两个新的警告。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xxx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvX&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Compile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvY&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(Compile.FileName)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Warning&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(WalterlvX)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Warning&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(WalterlvY)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-24-20-20-01.png&quot; alt=&quot;新增的警告&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvX&lt;/code&gt; 集合的时候，我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(Compile)&lt;/code&gt; 来获取所有需要编译的文件。&lt;/p&gt;

&lt;p&gt;在定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvY&lt;/code&gt; 集合的时候，我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;%(Compile.FileName)&lt;/code&gt; 来获取编译文件的文件名。&lt;/p&gt;

&lt;p&gt;于是，你在警告信息中看到的两个警告信息里面，一个输出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 集合中每一项的标识符（通常是相对于项目文件的路径），另一个输出了每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 项中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileName&lt;/code&gt; 属性。&lt;code class=&quot;highlighter-rouge&quot;&gt;FileName&lt;/code&gt; 属性是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 会被 Microsoft.NET.Sdk 自动填充。&lt;/p&gt;

&lt;p&gt;需要注意，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt; 得到的项中某个属性为空，那么这一项在最终形成的新集合中是不存在的。&lt;/p&gt;

&lt;p&gt;所以，如果存在可能不存在的属性，那么建议先进行拼接再统一处理拼接后的值：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xxx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Walterlv&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Compile)=%(Compile.CopyToOutputDirectory)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Warning&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Walterlv)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CopyToOutputDirectory&lt;/code&gt; 不是一个总是会设置的属性。&lt;/p&gt;
</description>
        <pubDate>Wed, 24 Apr 2019 12:35:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-referencing-metadata.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-referencing-metadata.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 7-Zip 的命令行版本来压缩和解压文件</title>
        <description>&lt;p&gt;7-Zip 也有一个简短的名称 7z。它的原生 UI 确实不怎么好看，非常有年代感；不过 7-Zip 的强大之处不在于 UI，而在于其算法和各种文件的支持情况。不过，7-Zip 提供了命令行的版本，让你摒除一切杂念，专心处理压缩文件的工作。&lt;/p&gt;

&lt;p&gt;本文介绍如何通过命令行来使用 7-Zip。因为使用命令行，所以你甚至可以自动化地完成压缩文件的各种处理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何找到-7-zip-的命令行版本&quot;&gt;如何找到 7-Zip 的命令行版本&lt;/h2&gt;

&lt;p&gt;请前往官方网站下载 7-Zip：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.7-zip.org/download.html&quot;&gt;7-Zip - Download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载安装完去其安装目录下可以找到 7-Zip 的命令行版本：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-24-08-23-43.png&quot; alt=&quot;7-Zip 的安装目录&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些文件作用分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7zFM.exe&lt;/code&gt; 7-Zip 文件管理器的主 UI，直接从开始菜单打开 7-Zip 时的 UI 界面。&lt;em&gt;依赖 7z.dll&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7zG.exe&lt;/code&gt; 7-Zip 的 GUI 模块，需要通过命令行指定参数调用。&lt;em&gt;依赖 7z.dll&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7-zip.dll&lt;/code&gt; 与 Windows Shell 以及 7zFM.exe 集成。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7z.exe&lt;/code&gt; 7-Zip 的命令行版本，需要通过命令行指定参数调用。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7z.dll&lt;/code&gt; 7-Zip 的核心执行引擎。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7z.sfx&lt;/code&gt; SFX 模块（Windows 版本）。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7zCon.sfx&lt;/code&gt; SFX 模块（控制台版本）。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;7-zip.chm&lt;/code&gt; 7-Zip 的帮助说明文件。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;命令行版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;7z.exe&lt;/code&gt; 不依赖与其他 dll，所以我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;7z.exe&lt;/code&gt; 文件拷出来即可使用完整的命令行版本的 7z。&lt;/p&gt;

&lt;h2 id=&quot;使用命令行操作-7zexe&quot;&gt;使用命令行操作 7z.exe&lt;/h2&gt;

&lt;p&gt;如果你希望使用 .NET/C# 代码来自动化地调用 7z.exe，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/run-commands-using-csharp&quot;&gt;编写 .NET/C# 代码来操作命令行程序 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文直接介绍 7z.exe 的命令行使用，你可以将其无缝地迁移至上面这篇博客中编写的 .NET/C# 代码中。&lt;/p&gt;

&lt;h3 id=&quot;解压一个文件&quot;&gt;解压一个文件&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;&amp;gt; 7z x {fileName} -o{outputDirectory}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; 表示解压一个文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{fileName}&lt;/code&gt; 是文件名称或者文件路径的占位符&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{outputDirectory}&lt;/code&gt; 是解压后文件夹的占位符，必须是一个不存在的文件夹。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-o&lt;/code&gt; 表示指定输出路径&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;特别注意：&lt;code class=&quot;highlighter-rouge&quot;&gt;-o&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;{outputDirectory}&lt;/code&gt; 之间是 &lt;strong&gt;没有空格&lt;/strong&gt; 的。&lt;/p&gt;

&lt;p&gt;一个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;&amp;gt; 7z x C:\Users\walterlv\demo.7z -oC:\Users\walterlv\demo
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;7z 的强大之处还有一点就是可以解压各种文件——包括解压安装包：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;&amp;gt; 7z x C:\Users\walterlv\nsis_installer_1.0.0.0.exe -oC:\Users\walterlv\nsis
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这也是为什么我们考虑使用 7z 来解压缩，而不是使用相关的 NuGet 包来调用。&lt;/p&gt;

&lt;h2 id=&quot;其他命令行操作&quot;&gt;其他命令行操作&lt;/h2&gt;

&lt;p&gt;运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;7z.exe&lt;/code&gt; 后可以看到命令行中列出了可用的命令行命令：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;a：将文件添加到压缩档案中
b：测试压缩或解压算法执行时的 CPU 占用
d：从压缩档案中删除文件
e：将压缩档案中的所有文件解压到指定路径，所有文件将输出到同一个目录中
h：计算文件的哈希值
i：显示有关支持格式的信息
l：列出压缩档案的内容
rn：重命名压缩档案中的文件
t：测试压缩档案的完整性
u：更新要进入压缩档案中的文件
x：将压缩档案中的所有文件解压到指定路径，并包含所有文件的完整路径
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面列出几个常用的命令。&lt;/p&gt;

&lt;h3 id=&quot;a-添加文件&quot;&gt;a 添加文件&lt;/h3&gt;

&lt;p&gt;如果你需要压缩文件，或者将文件添加到现有的压缩档案中，则使用此命令。&lt;/p&gt;

&lt;p&gt;将 subdir\ 文件夹中的所有文件加入到 walterlv.zip 文件中，所有的子文件和文件夹将会在压缩档案的 subdir 文件夹中：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;7z a walterlv.zip subdir\
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;将 subdir\ 文件夹中的所有文件加入到 walterlv.zip 文件中，所有的子文件和文件夹路径不会包含 subdir 前缀：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;7z a walterlv.zip .\subdir\*
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;d-删除文件&quot;&gt;d 删除文件&lt;/h3&gt;

&lt;p&gt;删除压缩档案 walterlv.zip 中的所有扩展名为 bak 的文件：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;7z d walterlv.zip *.bak -r
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;e-解压文件&quot;&gt;e 解压文件&lt;/h3&gt;

&lt;p&gt;相比于 x，此命令会将压缩档案中的所有文件输出到同一个目录中。&lt;/p&gt;
</description>
        <pubDate>Wed, 24 Apr 2019 01:55:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/command-line-usages-of-7z.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/command-line-usages-of-7z.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>ClearType 的原理：Windows 上文本的亚像素控制</title>
        <description>&lt;p&gt;有位小伙伴问我为什么他电脑上的文本看起来比较虚。我去看了下，发现他电脑上关掉了 ClearType。&lt;/p&gt;

&lt;p&gt;微软的 ClearType 技术通过控制亚像素来使得文本显示更为清晰。本文代理了解 Windows 系统上的文本是如何通过亚像素控制使得显示更为清晰的。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;cleartype-打开和关闭之后的效果&quot;&gt;ClearType 打开和关闭之后的效果&lt;/h2&gt;

&lt;p&gt;看下图！同样的文本，在不同大小下以及开关 ClearType 下的显示效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-17-39-16.png&quot; alt=&quot;文本效果预览&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你应该能注意到，第 0x00 行，第 0x02 行是比较模糊的，第 0x01 行和第 0x03 行会更清晰一些。&lt;/p&gt;

&lt;p&gt;如果你看不出来我说的效果，那么你需要调整你看图的姿势：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;请确保以 100% 比例显示此图片，正在在电脑上看我博客的时候，就会以 100% 比例显示；&lt;/li&gt;
  &lt;li&gt;如果你看博客的显示器 DPI 不是 100%，那么也看不出效果，建议在一个 100% DPI 的显示器设备上看。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果依然看不出来，至少你能感受到第 0x01 行和第 0x03 行的文本会更亮一些。&lt;/p&gt;

&lt;p&gt;现在，我们将图片放大。就像下面这张图片一样，左边一半是没有启用 ClearType 的文本，右边是启用了 ClearType 的文本。我将他们放到了一张图片上以便更容易比较效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-17-29-33.png&quot; alt=&quot;放大后的文本预览&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以注意到，没有开启 ClearType 的文本，其发虚的边框周围是灰色；而开启了 ClearType 的文本，其发虚的边框周围是彩色。&lt;/p&gt;

&lt;h2 id=&quot;如何显示清晰的线条&quot;&gt;如何显示清晰的线条&lt;/h2&gt;

&lt;h3 id=&quot;像素内的-rgb&quot;&gt;像素内的 RGB&lt;/h3&gt;

&lt;p&gt;在开始显示线条之前，我们来看看显示器如何显示一个像素。下图是我放大的一个像素内的灯管。这是一种主流显示器上像素内的 RGB 排列。这三个灯管同时以规定的最大值亮起，我们将看到白色。当然，我放大这么大你是看不出来白色的，需要足够小才行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-17-52-56.png&quot; alt=&quot;一个像素&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们缩小一点，观察 4×4 个像素：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-17-52-33.png&quot; alt=&quot;4×4 个像素&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;清晰显示-1px-线条&quot;&gt;清晰显示 1px 线条&lt;/h3&gt;

&lt;p&gt;我在另一篇博客中说过如何清晰显示一个线条：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/draw-aligned-lines-using-guidelineset&quot;&gt;WPF 绘制对齐像素的清晰显示的线条&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要清晰显示 1 像素宽度的竖线，我们需要对齐像素显示，即在整数像素上显示这根线条。于是，我们需要点亮这一列像素中的所有 RGB：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-17-58-05.png&quot; alt=&quot;亮起一列像素中的全部 RGB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;嗯，最终看起来会像这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-01-06.png&quot; alt=&quot;清晰显示的白色线条&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;清晰显示-13-px-线条&quot;&gt;清晰显示 1.3 px 线条&lt;/h3&gt;

&lt;p&gt;那么接下来，如何清晰显示 1.33 像素宽度的竖线呢？&lt;/p&gt;

&lt;p&gt;传统方法是借用旁边像素，点亮旁边像素 33% 的亮度，于是线条大概是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-02-08.png&quot; alt=&quot;传统 1.33 像素的线条&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对应到灯管，大概是这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-03-05.png&quot; alt=&quot;传统 1.33 像素亮起的灯管&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，这样显示 1.33 像素使用了 2 个像素的宽度，用了 6 个灯管。&lt;/p&gt;

&lt;p&gt;然而如果亮起的灯管是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-04-07.png&quot; alt=&quot;亚像素控制的 1.33 像素灯管&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为现在依然是 RGB 三个灯管紧挨着一起量的，所以人类依然会看出白色来。由于此时灯管亮起的依然是硬边缘，所以依然清晰。&lt;/p&gt;

&lt;p&gt;要控制这样亮起灯管，我们需要在左边像素显示白色，右边像素显示红色。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-05-49.png&quot; alt=&quot;亚像素控制的 1.33 像素的线条&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这个线条中，右边的线条因为是红色，所以只会亮起红色灯管，而这是最靠近左边像素的灯管。&lt;/p&gt;

&lt;h3 id=&quot;清晰显示-17-px-线条&quot;&gt;清晰显示 1.7 px 线条&lt;/h3&gt;

&lt;p&gt;同样的，如果要清晰显示 1.67 像素宽度的竖线，我们需要使用 5 列灯管：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-07-44.png&quot; alt=&quot;亚像素控制的 1.67 像素灯管&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，我们不止借用了右边像素显示红色，还借用了左边像素显示蓝色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-08-20.png&quot; alt=&quot;亚像素控制的 1.67 像素的线条&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，也可以是在右边借用一个黄色的像素，也就是亮起 RG 两列灯管。借用哪一边取决于需要从像素的哪个位置开始显示。&lt;/p&gt;

&lt;h2 id=&quot;文本的亚像素控制&quot;&gt;文本的亚像素控制&lt;/h2&gt;

&lt;p&gt;由于文本的显示不像简单图形显示可以随意选取起点，文本因为图形非常复杂，为了保持文本形状不至于变形太多，任何位置开始显示一个像素的起点都是可能的，所以文本需要更多地选择借用左右像素的相邻灯管。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-17-35.png&quot; alt=&quot;使用了 ClearType 效果的单个文字&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这张图中，果字最中间的竖线，借用了左侧像素的蓝色灯管，借用了右侧像素的红色和绿色灯管。横线的最右边，借用了右侧像素的红色灯管。其他像素以此类推。&lt;/p&gt;

&lt;h2 id=&quot;cleartype&quot;&gt;ClearType&lt;/h2&gt;

&lt;p&gt;实际上，本文使用的显示器是 RGB 排列的，其他显示器还有更多像素排列方式，Windows 系统会自动根据像素排列方式选择合适的 ClearType 借用临近灯管的方式。&lt;/p&gt;

&lt;p&gt;不过，识别错也是常态，你需要在 Windows 10 搜索框中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClearType&lt;/code&gt; 打开 ClearType 的设置界面，选择最清晰的显示文字来调整这样的错误识别。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-15-43.png&quot; alt=&quot;ClearType 设置 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-23-19-15-48.png&quot; alt=&quot;ClearType 设置 2&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Apr 2019 11:17:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-subpixel-rendering-of-text.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-subpixel-rendering-of-text.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>C#/.NET 使用 git 命令行来操作 git 仓库</title>
        <description>&lt;p&gt;我们可以在命令行中操作 git，但是作为一名程序员，如果在大量重复的时候还手动敲命令行，那就太笨了。&lt;/p&gt;

&lt;p&gt;本文介绍使用 C# 编写一个 .NET 程序来自动化地使用 git 命令行来操作 git 仓库。&lt;/p&gt;

&lt;p&gt;这是一篇很基础的入门文章。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;最简单的运行-git-命令的代码&quot;&gt;最简单的运行 git 命令的代码&lt;/h2&gt;

&lt;p&gt;在 .NET 中，运行一个命令只需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 开启一个子进程就好了。于是要运行一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt; 命令，我们其实只需要这句足以：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，直接能简写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt; 是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;git.exe&lt;/code&gt; 在我的环境变量里面，一般开发者在安装 Git 客户端的时候，都会自动将此命令加入到环境变量。如果没有，你需要使用完整路径 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\Git\mingw64\bin\git.exe&lt;/code&gt; 只是每个人的路径可能不同，所以这是不靠谱的。&lt;/p&gt;

&lt;h2 id=&quot;允许获得命令的输出&quot;&gt;允许获得命令的输出&lt;/h2&gt;

&lt;p&gt;对于上节中写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt;，你一眼就能看出来这是完全没有用的代码。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;git status&lt;/code&gt; 命令只是获得仓库当前的状态，这个命令完全不影响仓库，只是为了看状态的。&lt;/p&gt;

&lt;p&gt;所以，命令最好要能够获得输出。&lt;/p&gt;

&lt;p&gt;而要获得输出，你需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessStartInfo&lt;/code&gt; 来指定如何启动一个进程。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecutablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CreateNoWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UseShellExecute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要设置至少这四个属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNoWindow&lt;/code&gt; 表示不要为这个命令单独创建一个控制台窗口
    &lt;ul&gt;
      &lt;li&gt;实际上如果使用此代码的程序也是一个控制台程序，这句是没有必要的，因为子进程会共用父进程的控制台窗口；但是对于 GUI 程序来说，这句还是很重要的，这可以避免在执行命令的过程中意外弹出一个黑色的控制台窗口出来。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectStandardOutput&lt;/code&gt; 进行输出的重定向
    &lt;ul&gt;
      &lt;li&gt;这是一定要设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 的属性，因为我们希望拿到命令的输出结果。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WorkingDirectory&lt;/code&gt; 设置工作路径
    &lt;ul&gt;
      &lt;li&gt;本来这是一个可选设置，不过对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt; 命令来说，一般都是对一个已有的 git 仓库进行操作，所以当然要指定一个合理的 git 仓库了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UseShellExecute&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 表示不要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShellExecute&lt;/code&gt; 函数创建进程
    &lt;ul&gt;
      &lt;li&gt;此属性的详细说明，请阅读我的另一篇博客：&lt;a href=&quot;/post/use-shell-execute-in-process-start-info&quot;&gt;ProcessStartInfo 中的 UseShellExecute - 吕毅&lt;/a&gt;。&lt;/li&gt;
      &lt;li&gt;这里我们必须指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，因为要重定向输出的话，这是唯一有效值。顺便一提，此属性如果不设置，默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;commandrunner&quot;&gt;CommandRunner&lt;/h2&gt;

&lt;p&gt;为了方便起见，我将全部运行一个命令的代码封装到了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandRunner&lt;/code&gt; 的类当中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.GitDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandRunner&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecutablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExecutablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecutablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;CreateNoWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;UseShellExecute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StartInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StandardOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;测试与结果&quot;&gt;测试与结果&lt;/h2&gt;

&lt;p&gt;以上 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandRunner&lt;/code&gt; 命令的使用非常简单，&lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来之后，得到一个可以用来执行命令的实例，然后每次执行调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法传入参数即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\Developments\Blogs\walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add .&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;commit -m &quot;&quot;这是自动提交的&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果需要获得命令的执行结果，直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法的返回值即可。&lt;/p&gt;

&lt;p&gt;比如下面我贴了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数的完整代码，可以输出我仓库的当前状态：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.GitDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv 的自动 git 命令&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\Developments\Blogs\walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;按 Enter 退出程序……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-29-14-19-19.png&quot; alt=&quot;运行结果&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 21 Apr 2019 12:36:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/run-commands-using-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/run-commands-using-csharp.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>如何快速自定义 Visual Studio 中部分功能的快捷键</title>
        <description>&lt;p&gt;Visual Studio 中有些自带的快捷键与现有软件有冲突，那么如何修改这些快捷键让这些功能正常工作起来呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;打开快捷键设置界面&quot;&gt;打开快捷键设置界面&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 中打开 “工具 -&amp;gt; 选项”，打开选项设置界面。在其中找到 “环境 -&amp;gt; 键盘” 项。我们设置快捷键的地方就在这里。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-20-18-50-04.png&quot; alt=&quot;工具 -&amp;gt; 选项 -&amp;gt; 环境 -&amp;gt; 键盘&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;修改一个现有功能的快捷键&quot;&gt;修改一个现有功能的快捷键&lt;/h2&gt;

&lt;p&gt;默认情况下，在 Visual Studio 2019 中快速重构的快捷键是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt;。然而，使用中文输入法的各位应该非常清楚，&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt; 是输入法切换中英文符号的快捷键。&lt;/p&gt;

&lt;p&gt;于是，当使用中文输入法的时候，实际上是无法通过按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt; 来完成快速重构的。我们需要修改快捷键来避免这样的冲突。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-11-46-21.png&quot; alt=&quot;使用 Ctrl+. 来进行快速重构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在“新快捷键”那个框框中，按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt;，正常会在“快捷键的当前使用对象”框中出现此快捷键的功能。不过，如果快捷键已经与输入法冲突，则不会出现，你需要先切换至英文输入法以避免此冲突。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-12-20-03.png&quot; alt=&quot;显示此快捷键的当前功能&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过“快捷键的当前使用对象”下拉框，我们可以得知功能的名称，下拉框中的每一项都是此快捷键的功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-12-22-27.png&quot; alt=&quot;快捷键的当前使用对象&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们需要做的是，搜索这些功能，并为这些功能分配新的快捷键。每一个我们关心的功能都这么设置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-12-29-16.png&quot; alt=&quot;设置快捷键&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是新快捷键就设置好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-12-26-49.png&quot; alt=&quot;新分配的快捷键&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，可以使用新的快捷键来操作这些功能了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-21-20-06-38.png&quot; alt=&quot;可以使用新的快捷键&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/identifying-and-customizing-keyboard-shortcuts-in-visual-studio?view=vs-2019&quot;&gt;Identify and customize keyboard shortcuts - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 21 Apr 2019 12:06:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/customizing-keyboard-shortcuts-in-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/customizing-keyboard-shortcuts-in-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>WPF 像素着色器入门：使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码</title>
        <description>&lt;p&gt;HLSL，High Level Shader Language，高级着色器语言，是 Direct3D 着色器模型所必须的语言。WPF 支持 Direct3D 9，也支持使用 HLSL 来编写着色器。你可以使用任何一款编辑器来编写 HLSL，但 Shazzam Shader Editor 则是专门为 WPF 实现像素着色器而设计的一款编辑器，使用它来编写像素着色器，可以省去像素着色器接入到 WPF 所需的各种手工操作。&lt;/p&gt;

&lt;p&gt;本文是 WPF 编写 HLSL 的入门文章，带大家使用 Shazzam Shader Editor 来编写最简单的像素着色器代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载安装&quot;&gt;下载安装&lt;/h2&gt;

&lt;p&gt;实际上 Shazzam Shader Editor 有一段时间没有维护了，不过在 WPF 下依然是一个不错的编写 HLSL 的工具。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;于是去我的镜像地址下载：&lt;a href=&quot;https://github.com/walterlv/download/raw/master/Shazzam/Shazzam_v1.5.Setup.exe&quot;&gt;https://github.com/walterlv/download/raw/master/Shazzam/Shazzam_v1.5.Setup.exe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;已经没有官网了：shazzam-tool.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载完成之后安装到你的电脑上即可。&lt;/p&gt;

&lt;p&gt;Shazzam 是开源的，但是官方开源在 CodePlex 上，&lt;a href=&quot;https://archive.codeplex.com/?p=shazzam&quot;&gt;https://archive.codeplex.com/?p=shazzam&lt;/a&gt;，而 CodePlex 已经关闭。JohanLarsson 将其 Fork 到了 GitHub 上，&lt;a href=&quot;https://github.com/JohanLarsson/Shazzam&quot;&gt;https://github.com/JohanLarsson/Shazzam&lt;/a&gt;，不过几乎只有代码查看功能而不提供维护。&lt;/p&gt;

&lt;h2 id=&quot;shazzam-shader-editor&quot;&gt;Shazzam Shader Editor&lt;/h2&gt;

&lt;h3 id=&quot;主界面&quot;&gt;主界面&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-10-48-18.png&quot; alt=&quot;Shazzam 的主界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开 Shazzam，左侧会默认选中 Sample Shaders 即着色器示例，对于不了解像素着色器能够做到什么效果的小伙伴来说，仅浏览这里面的特效就能够学到很多好玩的东西。&lt;/p&gt;

&lt;p&gt;旁边是 Tutorial 教程，这里的教程是配合 &lt;a href=&quot;https://www.amazon.com/HLSL-Pixel-Shaders-XAML-Developers/dp/144931984X&quot;&gt;HLSL and Pixel Shaders for XAML Developers&lt;/a&gt; 这本书来食用的，所以如果希望能够系统地学习 HLSL，那么读一读这本书跟着学习里面的代码吧！&lt;/p&gt;

&lt;p&gt;左边的另一个标签是 Your Folder，可以放平时学习 HLSL 时的各种代码，也可以是你的项目代码，这里会过滤出 &lt;code class=&quot;highlighter-rouge&quot;&gt;.fx&lt;/code&gt; 文件用于编写 HLSL 代码。&lt;/p&gt;

&lt;p&gt;如果你打开关于界面，你可以看到这款软件很用心地在关于窗口背后使用了 &lt;a href=&quot;https://affirmaconsulting.wordpress.com/2010/12/30/tool-for-developing-hlsl-pixel-shaders-for-wpf-and-silverlight/&quot;&gt;TelescopicBlur&lt;/a&gt; 特效，这是一个 PS_3 特效，后面会解释其含义。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-10-52-23.png&quot; alt=&quot;加了特效的关于界面&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;公共设置&quot;&gt;公共设置&lt;/h3&gt;

&lt;p&gt;依然在左侧，可以选择 Settings 设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-11-41.png&quot; alt=&quot;Shazzam 设置&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;目标框架-target-framework&quot;&gt;目标框架 Target Framework&lt;/h4&gt;

&lt;p&gt;WPF 自 .NET Framework 4.0 开始支持 PS_3，当然也包括现在的 .NET Core 3.0。如果你不是为了兼容古老的 .NET Framework 3.5 或者更早版本，则建议将默认的 PS_2 修改为 PS_3。因为 PS_2 的限制还是太多了。&lt;/p&gt;

&lt;p&gt;关于 PS_3 相比于此前带来的更新可以查看微软的官方文档了解：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-3-0&quot;&gt;ps_3_0 - Windows applications - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;h4 id=&quot;生成的命名空间-generated-namespace&quot;&gt;生成的命名空间 Generated Namespace&lt;/h4&gt;

&lt;p&gt;默认是 Shazzam，实际上在接入到你的项目的时候，这个命名空间肯定是要改的，所以建议改成你项目中需要使用到的命名空间。比如我的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Effects&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;改好之后，如果你编译你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.fx&lt;/code&gt; 文件，也就是编写了 HLSL 代码的文件，那么顺便也会生成一份使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Effects&lt;/code&gt; 命名空间的 C# 代码便于你将此特效接入到你的 WPF 应用程序中。&lt;/p&gt;

&lt;h4 id=&quot;缩进-indentation&quot;&gt;缩进 Indentation&lt;/h4&gt;

&lt;p&gt;默认的缩进是 Tab，非常不清真，建议改成四个空格。&lt;/p&gt;

&lt;h4 id=&quot;默认动画时长-default-animation-length&quot;&gt;默认动画时长 Default Animation Length&lt;/h4&gt;

&lt;p&gt;如果你的特效是为了制作动画（实际上在 Shazzam 中编写的 HLSL，任何一个寄存器（变量）都可以拿来做动画），那么此值将给动画设置一个默认的时长。&lt;/p&gt;

&lt;p&gt;相比于前面的所有设置，这个设置不会影响到你的任何代码，只是决定你预览动画效果时的时长，所以设置多少都没有影响。&lt;/p&gt;

&lt;h2 id=&quot;编写-hlsl-代码&quot;&gt;编写 HLSL 代码&lt;/h2&gt;

&lt;h3 id=&quot;hlsl-代码窗格&quot;&gt;HLSL 代码窗格&lt;/h3&gt;

&lt;p&gt;实际上本文不会教你编写任何 HLSL 代码，也不会进行任何语法入门之类的，我们只需要了解 Shazzam 是如何帮助我们为 WPF 程序编写像素着色器代码的。&lt;/p&gt;

&lt;p&gt;将你的视线移至下方富含代码的窗格，这里标记着 XXX.fx 的标签就是 HLSL 代码了。大致浏览一下，你会觉得这风格就是 C 系列的语言风格，所以从学校里出来的各位应该很有亲切感，上手难度不高。&lt;/p&gt;

&lt;p&gt;按下 F5，即可立即编译你的 HLSL 代码，并在界面上方看到预览效果。别说你没有 HLSL 代码，前面我们可是打开了那么多个示例教程呀。&lt;/p&gt;

&lt;h3 id=&quot;预览调节窗格&quot;&gt;预览调节窗格&lt;/h3&gt;

&lt;p&gt;确保你刚刚使用 F5 编译了你的 HLSL 代码。这样，你就能在这个窗格看到各种预览调节选项。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-22-28.png&quot; alt=&quot;预览调节&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可以直接拉动拉杆调节参数范围，也可以直接开启一个动画预览各种值的连续变化效果。&lt;/p&gt;

&lt;h3 id=&quot;生成的-c-代码&quot;&gt;生成的 C# 代码&lt;/h3&gt;

&lt;p&gt;继续切换一个标签，你可以看到 Shazzam 为你生成的 C# 代码。实际上稍后你就可以直接使用这份代码驱动起你刚刚编写的特效。&lt;/p&gt;

&lt;p&gt;代码风格使用了我们刚刚设置的一些全局参数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-24-40.png&quot; alt=&quot;生成的 C# 代码&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;将像素着色器放到-wpf-项目中&quot;&gt;将像素着色器放到 WPF 项目中&lt;/h2&gt;

&lt;p&gt;将像素着色器放到 WPF 项目中需要经过两个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;找到生成的像素着色器文件，并放入 WPF 工程中；&lt;/li&gt;
  &lt;li&gt;修改像素着色器的生成方式。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;将特效放入到你的-wpf-项目中&quot;&gt;将特效放入到你的 WPF 项目中&lt;/h3&gt;

&lt;p&gt;我们需要将两个文件加入到你的 WPF 程序中：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ps&lt;/code&gt; 文件，即刚刚的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.fx&lt;/code&gt; 文件编译后的像素着色器文件；&lt;/li&gt;
  &lt;li&gt;一份用于驱动此像素着色器的 C# 代码。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这些文件都可以使用以下方法找到：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;请前往 &lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%\Shazzam\GeneratedShaders&lt;/code&gt; 文件夹；&lt;/li&gt;
  &lt;li&gt;根据名称变化规则找到对应的文件夹：
    &lt;ul&gt;
      &lt;li&gt;注意命名，如果你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.fx&lt;/code&gt; 文件命名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.fx&lt;/code&gt;，那么生成的文件就会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvEffect&lt;/code&gt; 文件夹下&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;进入刚刚找到的 XxxEffect 文件夹，里面有你需要的所有文件：
    &lt;ul&gt;
      &lt;li&gt;一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ps&lt;/code&gt; 文件&lt;/li&gt;
      &lt;li&gt;一个 C# 文件（以及 VB 文件）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;随后，将这两份文件一并加入到你的 WPF 项目工程文件中。&lt;/p&gt;

&lt;p&gt;但是，请特别注意路径！留意你的 C# 代码，里面是编写了像素着色器的路径的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果你的程序集名称是其他名称，需要修改下面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Effects&lt;/code&gt; 的部分改成你的程序集名称；&lt;/li&gt;
  &lt;li&gt;如果你放到了其他的子文件夹中，你也需要在下面 &lt;code class=&quot;highlighter-rouge&quot;&gt;/WalterlvEffect.ps&lt;/code&gt; 的前面加上子文件夹。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 记得修改程序集名称，以及 .ps 文件所在的文件夹路径！切记！&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pixelShader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UriSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/Walterlv.Effects;component/WalterlvEffect.ps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;修改像素着色器的生成方式&quot;&gt;修改像素着色器的生成方式&lt;/h3&gt;

&lt;p&gt;需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt; 方式编译此 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ps&lt;/code&gt; 文件到 WPF 项目中。&lt;/p&gt;

&lt;p&gt;如果你使用的是旧的项目格式，则右键此 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ps&lt;/code&gt; 文件的时候选择属性，你可以在 Visual Studio 的属性窗格的生成操作中将其设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-51-09.png&quot; alt=&quot;右键属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-55-28.png&quot; alt=&quot;使用 Resource 编译&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你使用的是 Sdk 风格的新项目格式，则在属性窗格中无法将其设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Resource&lt;/code&gt;，这个时候请直接修改 .csproj 文件，加上下面一行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.ps&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果不知道怎么放，我可以多贴一些 csproj 的代码，用于指示其位置：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;WinExe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseWPF&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseWPF&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.Demo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.ps&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在-wpf-程序中使用这个特效&quot;&gt;在 WPF 程序中使用这个特效&lt;/h2&gt;

&lt;p&gt;要在 WPF 程序中使用这个特效，则设置控件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Effect&lt;/code&gt; 属性，将我们刚刚生成的像素着色器对应 C# 代码的类名写进去即可。当然，需要在前面引入 XAML 命名空间。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.CloudTyping.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:effects=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Effects&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Effect&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;effects:WalterlvEffect&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Effect&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略了界面上的各种代码 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面是我将 Underwater 特效加入到我的云键盘窗口中，给整个窗口带来的视觉效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-17-11-46-35.png&quot; alt=&quot;云键盘的水下特效&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;入门总结&quot;&gt;入门总结&lt;/h2&gt;

&lt;p&gt;本文毕竟是一篇入门文章，没有涉及到任何的技术细节。你可以按照以下问题检查是否入门成功：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你能否成功安装并打开 Shazzam Shader Editor 软件？&lt;/li&gt;
  &lt;li&gt;你能否找到并打开一个示例像素着色器代码，并完成编译预览效果？&lt;/li&gt;
  &lt;li&gt;知道如何设置像素着色器使用 PS_3 版本吗？&lt;/li&gt;
  &lt;li&gt;尝试将一个示例像素着色器编译完并放入到你的 WPF 项目中。&lt;/li&gt;
  &lt;li&gt;尝试将特效应用到你的一个 WPF 控件中查看其效果。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://shazzam-tool.com/&quot;&gt;shazzam-tool.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.codeplex.com/?p=shazzam&quot;&gt;Shazzam Shader Editor - CodePlex Archive&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/JohanLarsson/Shazzam&quot;&gt;JohanLarsson/Shazzam: A fork of https://shazzam.codeplex.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://channel9.msdn.com/Shows/Continuum/Shazzam&quot;&gt;Shazzam - A Tool for Creating WPF Pixel Shader Effects - The Continuum Show - Channel 9&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-3-0&quot;&gt;ps_3_0 - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-ps-3-0&quot;&gt;ps_3_0 Registers - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 17 Apr 2019 06:32:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-wpf-pixel-shader-effects-using-shazzam-shader-editor.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-wpf-pixel-shader-effects-using-shazzam-shader-editor.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>directx</category>
        
      </item>
    
      <item>
        <title>如何在 MSBuild 的项目文件 csproj 中获取绝对路径</title>
        <description>&lt;p&gt;通常我们能够在 csproj 文件中仅仅使用相对路径就完成大多数的编译任务。但是有些外部命令的执行需要用到绝对路径，或者对此外部工具来说，相对路径具有不同的含义。这个时候，就需要将相对路径在 csproj 中转换为绝对路径来使用。&lt;/p&gt;

&lt;p&gt;本文介绍如何在项目文件 csproj 中将一个相对路径转换为绝对路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 MSBuild 4.0 中，可以在 csproj 中编写调用 PowerShell 脚本的代码，于是获取一个路径的绝对路径就非常简单：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;System.IO.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'$(WalterlvRelativePath)'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体到 csproj 的代码中，是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvRelativePath&amp;gt;&lt;/span&gt;$(OutputPath)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvRelativePath&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_WalterlvAbsolutePath&amp;gt;&lt;/span&gt;$([System.IO.Path]::GetFullPath($(WalterlvRelativePath)))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_WalterlvAbsolutePath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，就可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(_WalterlvAbsolutePath)&lt;/code&gt; 属性来获取绝对路径。&lt;/p&gt;

&lt;p&gt;你可以阅读我的其他篇博客了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(OutputPath)&lt;/code&gt; 其实最终都会是相对路径：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-properties-that-affetcs-project-output-path&quot;&gt;如何更精准地设置 C# / .NET Core 项目的输出路径？（包括添加和删除各种前后缀） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/1251198/6233938&quot;&gt;How can I get MSBUILD to evaluate and print the full path when given a relative path? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/sayedihashimi/4366619&quot;&gt;Demonstrates how you can convert a relative path to an absolute path in MSBuild&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 16 Apr 2019 10:16:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-absolute-path-in-msbuild-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-absolute-path-in-msbuild-targets.html</guid>
        
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>MSBuild 如何编写带条件的属性、集合和任务 Condition？</title>
        <description>&lt;p&gt;在项目文件 csproj 中，通过编写带条件的属性（PropertyGroup）、集合（ItemGroup）和任务（Target）可以完成更加复杂的项目文件的功能。&lt;/p&gt;

&lt;p&gt;本文介绍如何编写带条件的 MSBuild 项。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;condition&quot;&gt;Condition&lt;/h2&gt;

&lt;p&gt;如果要给你的 MSBuild 项附加条件，那么加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Condition&lt;/code&gt; 特性即可。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Condition&lt;/code&gt; 可以写在任何地方，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 或者内部的一个属性或一个项或者一个任务等。&lt;/p&gt;

&lt;p&gt;下面这段代码表示在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 配置下计算一个属性的值，而这个逗比属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoubiNames&lt;/code&gt; 的属性仅在此属性从未被指定过值的时候赋一个值 &lt;code class=&quot;highlighter-rouge&quot;&gt;吕毅&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)' == 'Debug' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubiNames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(DoubiNames)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;吕毅&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DoubiNames&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在单引号的前后，等号这些运算符的前后空格可加可不加，没有影响。&lt;/p&gt;

&lt;h2 id=&quot;单引号&quot;&gt;单引号&lt;/h2&gt;

&lt;p&gt;在上面的例子中，我们给条件中的所有字符串加上了包裹的单引号。&lt;/p&gt;

&lt;p&gt;单引号对于简单的字母数字字符串是不必要的，对于布尔值来说也是不必要的。但是，对于空值来说，是必须加上的，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;''&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;-和-&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 符号左右两侧的字符串如果相等，则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，否则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt; 符号左右两侧的字符串如果相等，则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，否则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot; $(Configuration) == 'Debug' &quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;---&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;用于比较数值上的大小关系。当然，在项目文件中，用于表示数值的字符串在此操作符下表示的就是数值。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;左右两侧比较的字符串必须是表示数值的字符串，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;123&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x7b&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;只能是十进制或者十六进制字符串，而十六进制字符串必须以 &lt;code class=&quot;highlighter-rouge&quot;&gt;0x&lt;/code&gt; 开头；&lt;/li&gt;
  &lt;li&gt;由于此比较是写在 XML 文件中的，所以必须转义，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; 需要写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;lt;&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; 需要写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;gt;&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;exists-hastrailingslash&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exists&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;HasTrailingSlash&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exists&lt;/code&gt; 判断文件或者文件夹是否存在。存在则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，否则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot; Exists('Foo\walterlv.config') &quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot; Exists('Foo\WalterlvFolder') &quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot; Exists('$(WalterlvFile)') &quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HasTrailingSlash&lt;/code&gt; 如果字符串的尾部包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;\&lt;/code&gt; 字符串，则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，否则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot;!HasTrailingSlash($(OutputPath))&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;与或非and-or-&quot;&gt;与或非：&lt;code class=&quot;highlighter-rouge&quot;&gt;And&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Or&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;!&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;就是计算机中常见的与或非的机制。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubiNames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(DoubiNames)' == '吕毅' Or '$(DoubiNames)' == '林德熙' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;组队逗比&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DoubiNames&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;组合&quot;&gt;组合：&lt;code class=&quot;highlighter-rouge&quot;&gt;()&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;就是计算机中通常用于修改运算优先级的括号，这可以先计算括号内的布尔结果。&lt;/p&gt;

&lt;h2 id=&quot;if-条件if&quot;&gt;if 条件：&lt;code class=&quot;highlighter-rouge&quot;&gt;$if$&lt;/code&gt;&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Condition=&quot; $if$ ( %expression% ), $else$, $endif$ &quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions&quot;&gt;MSBuild Conditions - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/6709057/6233938&quot;&gt;Visual Studio Project/Item Template Parameter Logic - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 16 Apr 2019 09:27:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-write-msbuild-conditions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-write-msbuild-conditions.html</guid>
        
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>NullReferenceException，就不应该存在！</title>
        <description>&lt;p&gt;如果要你说出 .NET 中的三个异常，&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 一定会成为其中一个；如果说出 .NET 中的一个异常，&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 也会被大多数人说出来。它让这么多人印象深刻，是因为它在项目中实在是太常见了，常见到每一个 C#/.NET 入门者必然会遇到。&lt;/p&gt;

&lt;p&gt;然而，这个异常本不应该存在！&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;null&quot;&gt;null&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/&quot;&gt;The worst mistake of computer science - Lucidchart&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.jobbole.com/93667/&quot;&gt;计算机科学中的最严重错误，造成十亿美元损失 - 文章 - 伯乐在线&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nullreferenceexception-的可恨之处&quot;&gt;NullReferenceException 的可恨之处&lt;/h2&gt;

&lt;p&gt;你说 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 可以告诉你程序中某个字段为 null，告诉你程序发生了 BUG。&lt;/p&gt;

&lt;p&gt;可是这是真的吗？说真的一定是因为用 Visual Studio 调试了，Visual Studio 告诉了我们异常发生在哪一句，哪个字段为 null。然而从真实用户或其他日志那里收集回来的数据是没有也不可能有这些信息的。这是因为 &lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 异常除了调用栈（StackTrace）之外不能提供其他额外的异常信息&lt;/strong&gt;，连变量或字段名都不能提供。于是，当从异常日志准备分析异常原因的时候，只能猜，猜到底为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的是谁！&lt;/p&gt;

&lt;p&gt;另外，&lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 异常发生的地方一定不是真正出错的地方&lt;/strong&gt;！因为我们尝试去调用某个属性或方法时假设了它不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，这意味着它为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 就是个错误。但是，从异常的调用栈中我们却找不到任何痕迹能够告诉我们是哪里给它设置成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;（或者是从未赋值过）。现在，又只能猜，猜到底是什么时候通过什么方式将字段设为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;！&lt;/p&gt;

&lt;p&gt;举个例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 可以在任何时候被任何方法调用，指不定某个时候 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 就被设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 了。那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomething&lt;/code&gt; 被调用的时候，直接就会抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;。这个方法比较简单，我们猜 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 基本不会有问题了，方法复杂一点儿就难猜了。然而真正让 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的罪魁祸首就找不到了，因为它发生在 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;总结起来，可恨之处有亮点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不能知道为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的是哪个变量、字段或属性；&lt;/li&gt;
  &lt;li&gt;不能知道为什么为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而这两点直接与异常机制相悖。异常就是要提供足够我们诊断错误的信息，让我们在开发中避免发生这样的错误。&lt;/p&gt;

&lt;h2 id=&quot;nullreferenceexception-的替代方案&quot;&gt;NullReferenceException 的替代方案&lt;/h2&gt;

&lt;p&gt;既然 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 没能给我们提供足够的信息，那么我们就自己来提供这些信息。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt; 就是一个不错的替代异常，说它好因为有两点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在错误发生的最开始就报告了错误，避免错误的蔓延。&lt;br /&gt;
  因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 中发生了异常后，获取到的调用栈是导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的调用栈。&lt;/li&gt;
  &lt;li&gt;告知了为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的参数名称。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;靠以上两点，当发生异常时，我们能唯一确定 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的原因，而这才是本质错误。&lt;/p&gt;

&lt;p&gt;可是，如果并不是参数问题导致了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，那我们还能用什么异常呢？&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt; 是个不错的方案，它的默认异常提示语是“&lt;em&gt;对象当前的状态使得该操作无效&lt;/em&gt;”。当程序此时此刻的状态让我们获取不到某个数据致使数据为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时，可以写一个新的提示语告知此时到底是什么样的状态错误才使得获取到的数据为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。当然，这比 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt; 的信息准确性还是差了点儿。&lt;/p&gt;

&lt;p&gt;当然，还有一个替代方案，就是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine(_value.Length);&lt;/code&gt; 之前先对 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 判断。可是，你能说出 &lt;code class=&quot;highlighter-rouge&quot;&gt;_value&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 代表什么意义吗？为什么为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时不应该输出？如果这个问题回答不上来，那么你的这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 判断为你的程序埋藏了一个更深的 BUG——&lt;strong&gt;当用户反馈软件行为不正常时，你甚至连异常信息都没收集到&lt;/strong&gt;！硕大一个程序，你&lt;strong&gt;甚至都无法定位到底是哪个模块发生了错误&lt;/strong&gt;！！！&lt;/p&gt;

&lt;h2 id=&quot;对待-null建议的约定&quot;&gt;对待 null，建议的约定&lt;/h2&gt;

&lt;p&gt;当了解了 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 的缺陷，再了解了其替代方案后，其实我们会发现一个问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;其实&lt;strong&gt;多数时候根本就不应该存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 带来了两个困惑：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;意义不明确。相比于异常，&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 并不能告知我们到底发生了什么。&lt;/li&gt;
  &lt;li&gt;使用方不知道究竟应不应该判空，也难以理清楚判空究竟意味着什么。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，为了解决这些困惑，我建议在开发中以如下方式对待我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;对任何可被外部模块调用的方法的参数进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 判断，并在参数为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt;。&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;不要在方法中返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。如果你无法根据现有状态完成方法承诺的任务，请抛出具体的异常并给出真实的原因。&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;如果确实要用 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 在程序中代表某种状态，请确定这能够代表某种唯一确定的状态，并强制要求使用方判空。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其中，对于第 2 点，不用担心异常导致雪崩，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;try-catch-finally&lt;/code&gt; 就是用来恢复错误防止雪崩的，在需要防止雪崩的地方恢复错误即可。但要注意异常依然需要报告，可由程序统一处理这些未经处理的异常。&lt;/p&gt;

&lt;p&gt;对于第 3 点，&lt;code class=&quot;highlighter-rouge&quot;&gt;JetBrains&lt;/code&gt; 为我们提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;JetBrains.Annotations&lt;/code&gt;，这是一组 100+ 个的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，以 NuGet 包的形式提供。强烈建议在 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 代表了某种特殊意义的地方标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;[CanBeNull]&lt;/code&gt;；这样，ReSharper 插件将提醒我们这些地方必须要进行判空。C# 8.0 极有可能为我们带来“可空引用类型”或者“非空引用类型”；如果真的带来了，这将比 &lt;code class=&quot;highlighter-rouge&quot;&gt;JetBrains.Annotations&lt;/code&gt; 拥有更大的强制性，帮助我们避免出现意外的 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 引用，帮助我们在可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的地方强制判空。再次重申：&lt;strong&gt;我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 一定是因为它代表了某种确定的特殊含义，而不是代表了一堆不明所以的错误！&lt;/strong&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 16 Apr 2019 02:47:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wipe-out-null-reference-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wipe-out-null-reference-exception.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>C# 跨设备前后端开发探索</title>
        <description>&lt;p&gt;每个人都拥有 &lt;strong&gt;好奇心&lt;/strong&gt;，好奇心驱使着我们总是去尝试做一些有趣的事情。&lt;/p&gt;

&lt;p&gt;带起你的好奇心，本文将使用 C# 开发各种各样好玩的东西。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;http://easinote.seewo.com/linkShare?id=7dc9e588977d4764b1ea1a4112716540&quot;&gt;&lt;img src=&quot;/static/posts/2019-04-14-17-10-52.png&quot; alt=&quot;C# 跨设备前后端开发探索&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本文内容已加入 &lt;a href=&quot;https://github.com/guangzhou-dotnet-club/coursewares/tree/master/%E7%AC%AC2%E5%B1%8A%E7%BA%BF%E4%B8%8B%E6%B2%99%E9%BE%99%4020190413&quot;&gt;2019 年 4 月 13 日的广州 .NET 俱乐部第 2 届线下沙龙&lt;/a&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;0x00-序章&quot;&gt;0x00 序章&lt;/h2&gt;

&lt;h3 id=&quot;好奇心&quot;&gt;好奇心&lt;/h3&gt;

&lt;p&gt;每个人都拥有 &lt;strong&gt;好奇心&lt;/strong&gt;，好奇心驱使着我们总是去尝试做一些有趣的事情。&lt;/p&gt;

&lt;p&gt;比如这件事：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-17-22-55.png&quot; alt=&quot;手机上打字慢&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在好奇心的驱使下，我们立刻 &lt;strong&gt;尝试&lt;/strong&gt; 我们的想法。&lt;/p&gt;

&lt;p&gt;我们需要用电脑打字，手机端出字；于是我们需要开发的是一款云输入法。而一个最简单的云驱动的软件需要至少一个 Web 后端、一个桌面端和一个移动端。&lt;/p&gt;

&lt;p&gt;还没开始呢，就这么复杂。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-12-37.png&quot; alt=&quot;需要至少三个端&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;先搞起来&quot;&gt;先搞起来&lt;/h3&gt;

&lt;p&gt;摆在我们面前的，有两条路可以选：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-19-52.png&quot; alt=&quot;先掌握所有理论知识再实践&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-20-13.png&quot; alt=&quot;无论什么技术，先搞起来&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果先搞起来，那么我们能够迅速出效果，出产品，出玩具，那么这种成就感会鼓励我们继续完善我们的代码，继续去做更多好玩的东西。&lt;/p&gt;

&lt;p&gt;而如果是先掌握所有理论知识再实践，这是我们从学校带来的学习方式，我们中的多数人在校期间就是这么学习的。虽然对学霸来说可以无视，但对于我们这样大多数的小伙伴来说，简直就是“从入门到放弃”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-learn-and-give-up.gif&quot; alt=&quot;从入门到放弃&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果先搞起来呢？如果我们连“入门”都不需要呢？是不是就不需要放弃了！&lt;/p&gt;

&lt;p&gt;怎么才能够先搞起来？我们需要调整一下心态——我们不是在学，而是在玩！&lt;/p&gt;

&lt;p&gt;我们需要做的是降低学习成本，甚至入门不学习，那么立刻就能玩起来！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-31-58.png&quot; alt=&quot;搞起来&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们有 C#，还有什么不能马上搞起来！&lt;/p&gt;

&lt;h2 id=&quot;0x01-c-跨设备前后端开发&quot;&gt;0x01 C# 跨设备前后端开发&lt;/h2&gt;

&lt;p&gt;打开 Visual Studio 2019，我们先搞起来！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-33-07.png&quot; alt=&quot;Visual Studio 2019&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;web-后端&quot;&gt;Web 后端&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-34-25.png&quot; alt=&quot;创建一个 Asp.NET Core Web 应用程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-34-30.png&quot; alt=&quot;输入项目的名称&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-34-35.png&quot; alt=&quot;选择 API 开发&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于简单的云服务来说，使用 Asp.NET Core 开发是非常简单快速的。你可以阅读林德熙的博客入门 Asp.NET Core 开发：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E4%BD%BF%E7%94%A8-asp-dotnet-core-%E5%81%9A-cs-%E7%A8%8B%E5%BA%8F.html&quot;&gt;win10 uwp 手把手教你使用 asp dotnet core 做 cs 程序 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;windows-桌面端&quot;&gt;Windows 桌面端&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-37-51.png&quot; alt=&quot;开发 Windows 桌面端&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们是要玩的呀，什么东西好玩。我们自己就是用户，用户看得到的部分才是最具有可玩性的。这就是指客户端或者 Web 前端。&lt;/p&gt;

&lt;p&gt;我们现在要拿 C# 写客户端，一般 C# 或者 .NET 的开发者拿什么来写桌面客户端呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;WPF 或者 Windows Forms 应用程序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-47-01.png&quot; alt=&quot;WPF 程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-47-06.png&quot; alt=&quot;Windows Forms 程序&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;公共代码&quot;&gt;公共代码&lt;/h3&gt;

&lt;p&gt;我们现在已经有至少两个端了。由于我们是同一个软件系统，所以实际上非常容易出现公共代码。典型的就是一些数据模型的定义，以及 Web API 的访问代码，还有一些业务需要的其他公共代码等等。&lt;/p&gt;

&lt;p&gt;所以，我们最好使用一个新的项目将这些代码整合起来。&lt;/p&gt;

&lt;p&gt;我们选用 .NET Standard 项目来存放这些代码，这样可以在各种 .NET 中使用这些库。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-50-07.png&quot; alt=&quot;.NET Standard 类库&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;控制台&quot;&gt;控制台&lt;/h3&gt;

&lt;p&gt;由于我们多数的代码都可以放到 .NET Standard 类库中，以确保绝大多数的代码都是平台和框架无关的，所以实际上我们在其他各个端项目中的代码会是很少的。&lt;/p&gt;

&lt;p&gt;这个时候，写一个控制台程序来测试我们的项目，控制台程序的部分其实只需要很少的用于控制控制台输入输出的代码，其他多数的代码例如用来访问 Web API 的代码都是不需要放在控制台项目中的，放到 .NET Standard 的类库中编写就可以做到最大程度的共用了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-50-24.png&quot; alt=&quot;控制台程序&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;ios-端&quot;&gt;iOS 端&lt;/h3&gt;

&lt;p&gt;接下来要完成这个云键盘程序，我们还需要开发一个移动端。使用 Xamarin 可以帮助我们完成这样的任务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-52-54.png&quot; alt=&quot;Xamarin.Forms&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-53-09.png&quot; alt=&quot;Xamarin 自定义键盘扩展&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于使用 Xamarin.Forms 开发一个键盘扩展，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/develop-ios-keyboard-extension-using-xamarin&quot;&gt;使用 Xamarin 开发 iOS 键盘扩展（含网络访问）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;web-前端&quot;&gt;Web 前端&lt;/h3&gt;

&lt;p&gt;于是，我们仅仅使用 C# 还有客户端开发者熟悉的 XAML 就开发出了三个端了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-55-17.png&quot; alt=&quot;三个端&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这三个端中，有两个都是客户端，于是就会存在向用户分发客户端的问题。虽然可以让用户去商店下载，但是提供一个官方下载页面可以让用户在一处地方找到所有端的下载和部署方法。&lt;/p&gt;

&lt;p&gt;这需要使用到前端。然而如何使用 C# 代码来编写去前端呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-57-35.png&quot; alt=&quot;如何使用 C# 来编写前端？&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用 CSHTML5！&lt;/p&gt;

&lt;p&gt;你可以前往 &lt;a href=&quot;http://www.cshtml5.com/&quot;&gt;CSHTML5 的官网&lt;/a&gt; 下载 Visual Studio 的插件，这样你就可以在 Visual Studio 中编写 CSHTML5 的代码了，还有设计器的支持。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-18-59-14.png&quot; alt=&quot;CSHTML5 如何编译 C# 和 XAML 代码&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;0x02-c-还能做什么&quot;&gt;0x02 C# 还能做什么？&lt;/h2&gt;

&lt;p&gt;于是我们使用 XAML + C# 就编写出了各个端了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-14-19-00-57.png&quot; alt=&quot;各个端&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果没有 GUI，那么跨平台将是非常容易的一件事情。例如我们想要在 Mac 电脑上也做一个打字发送的一方，那么一个控制台应用也是能够直接完成的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-04-15-08-09-34.png&quot; alt=&quot;没有 GUI，更容易跨平台&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过，这并不是说，我们只能通过控制台来开发桌面端应用。&lt;/p&gt;

&lt;p&gt;我们还有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia&quot;&gt;AvaloniaUI/Avalonia: A multi-platform .NET UI framework&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/other/wpf&quot;&gt;Xamarin 版的 WPF 桌面端&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/xamarin/mac/&quot;&gt;Xamarin.Mac - 开发 Mac 桌面端&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/other/gtk?tabs=windows&quot;&gt;GTK# - 开发 Linux 桌面端&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/other/tizen&quot;&gt;Tizen .NET - 开发三星物联网系统的 GUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;利用这些平台，我们能开发其他桌面平台的 GUI 客户端。&lt;/p&gt;

&lt;p&gt;另外，利用 ML.NET，我们还能用 C# 进行机器学习。可参见：&lt;a href=&quot;http://www.cnblogs.com/BeanHsiang/&quot;&gt;Bean.Hsiang - 博客园&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;利用 Roslyn，我们还能用直接做编译器，然后你还有什么不能做的？关于 Roslyn 的入门，可以阅读：&lt;a href=&quot;/post/posts-for-learning-dotnet-build-nuget-roslyn&quot;&gt;从零开始学习 dotnet 编译过程和 Roslyn 源码分析 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;还有 IoT。&lt;/p&gt;

&lt;p&gt;还有其他……&lt;/p&gt;

&lt;h2 id=&quot;0x03-终章&quot;&gt;0x03 终章&lt;/h2&gt;

&lt;p&gt;每个人都拥有 &lt;strong&gt;好奇心&lt;/strong&gt;，好奇心驱使着我们总是去尝试做一些有趣的事情。&lt;/p&gt;

&lt;p&gt;使用你熟悉的语言 C#，不需要太多额外的入门，即可玩转你身边各种你需要的技术栈，玩出各种各样你自己期望尝试开发的小东西。&lt;/p&gt;
</description>
        <pubDate>Tue, 16 Apr 2019 00:54:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/april-event-everything-can-be-done-using-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/april-event-everything-can-be-done-using-csharp.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>xamarin</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>从零开始学习 dotnet 编译过程和 Roslyn 源码分析</title>
        <description>&lt;p&gt;本文整理我和 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 学习的 dotnet 编译知识、Roslyn 源码分析知识，NuGet 知识。通过阅读本文可以从零散的碎片化博客中得到从零开始学习的轨迹。&lt;/p&gt;

&lt;p&gt;本文服务于 &lt;a href=&quot;/post/dotnet-build-and-roslyn-course-in-tech-summit-2018&quot;&gt;微软技术暨生态大会 2018 课程&lt;/a&gt;，你可以学习预编译框架相关的技术原理。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sourceyard-性能数据&quot;&gt;SourceYard 性能数据&lt;/h2&gt;

&lt;p&gt;SourceYard 通过将公共组件的源代码和产品源代码合并来提升性能。&lt;/p&gt;

&lt;p&gt;以下是这部分的性能数据：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%95%B0%E9%87%8F%E5%AF%B9%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;C# 程序集数量对软件启动性能的影响 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不过，程序集中的类的数量对启动性能没有影响：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E7%9B%B4%E6%8E%A5%E5%88%9B%E5%BB%BA%E5%A4%9A%E4%B8%AA%E7%B1%BB%E5%92%8C%E4%BD%BF%E7%94%A8%E5%8F%8D%E5%B0%84%E5%88%9B%E5%BB%BA%E7%B1%BB%E7%9A%84%E6%80%A7%E8%83%BD.html&quot;&gt;C# 直接创建多个类和使用反射创建类的性能 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sourcefusion-性能数据&quot;&gt;SourceFusion 性能数据&lt;/h2&gt;

&lt;p&gt;SourceFusion 的其中一个用途是收集原本会通过反射收集的类型信息。&lt;/p&gt;

&lt;p&gt;以下是这部分的性能数据：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90-%E5%8F%8D%E5%B0%84-VS-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-VS-%E9%A2%84%E7%BC%96%E8%AF%91.html&quot;&gt;C# 性能分析 反射 VS 配置文件 VS 预编译 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，如果不是收集而单单只是使用的话，这里是性能数据：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E7%9B%B4%E6%8E%A5%E5%88%9B%E5%BB%BA%E5%A4%9A%E4%B8%AA%E7%B1%BB%E5%92%8C%E4%BD%BF%E7%94%A8%E5%8F%8D%E5%B0%84%E5%88%9B%E5%BB%BA%E7%B1%BB%E7%9A%84%E6%80%A7%E8%83%BD.html&quot;&gt;C# 直接创建多个类和使用反射创建类的性能 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;dotnet-build-基础&quot;&gt;dotnet build 基础&lt;/h2&gt;

&lt;p&gt;你需要先了解 csproj 文件的结构，以便进行后续的学习：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E5%9C%A8%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E4%BD%BF%E7%94%A8%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD.html&quot;&gt;Roslyn 在项目文件使用条件判断 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在了解到 csproj 文件结构之后，你可以通过迁移一些项目，并确保他们编译通过来练习：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/use-msbuild-sdk-extras-for-wpf-and-uwp&quot;&gt;Sdk 风格的 csproj 对 WPF/UWP 支持不太好？有第三方 SDK 可以用！MSBuild.Sdk.Extras - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接着，csproj 中的重要内容 Target 对理解编译过程非常重要，因为它决定了如何编译这个项目：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-MSBuild-Copy-%E5%A4%8D%E5%88%B6%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn 如何使用 MSBuild Copy 复制文件 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/exec-task-of-msbuild-target&quot;&gt;如何使用 MSBuild Target（Exec）中的控制台输出 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/standard-error-warning-format&quot;&gt;如何在 MSBuild Target（Exec）中报告编译错误和编译警告 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更高级的 Target 用法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target（附各种自带的 Task） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E4%BD%BF%E7%94%A8-WriteLinesToFile-%E8%A7%A3%E5%86%B3%E5%8F%82%E6%95%B0%E8%BF%87%E9%95%BF%E6%97%A0%E6%B3%95%E4%BC%A0%E5%85%A5.html&quot;&gt;Roslyn 使用 WriteLinesToFile 解决参数过长无法传入 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;基于 Target 的一些应用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E5%A6%82%E4%BD%95%E5%9C%A8-Target-%E5%BC%95%E7%94%A8-xaml-%E9%98%B2%E6%AD%A2%E6%96%87%E4%BB%B6%E6%B2%A1%E6%9C%89%E7%BC%96%E8%AF%91.html&quot;&gt;Roslyn 如何在 Target 引用 xaml 防止文件没有编译 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当现有的知识和文档不足以帮助你完成现有功能的时候，也许你该考虑阅读官方源码了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk-en&quot;&gt;Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;还有一些 csproj 特性的使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-multiple-main-and-related-startup-codes&quot;&gt;.NET/C# 中你可以在代码中写多个 Main 函数，然后按需要随时切换 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/make-items-invisible-in-vs-solution-explorer&quot;&gt;在 Visual Studio 的解决方案资源管理器中隐藏一些文件 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/visualstudio/2016/08/01/share-code-with-add-as-link.html&quot;&gt;使用链接共享 Visual Studio 中的代码文件 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/vs/2017/09/26/wildcards-in-vs-projects.html&quot;&gt;为 Visual Studio 使用通配符批量添加项目文件 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E4%BD%BF%E7%94%A8-Directory.Build.props-%E7%AE%A1%E7%90%86%E5%A4%9A%E4%B8%AA%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE.html&quot;&gt;Roslyn 使用 Directory.Build.props 管理多个项目配置 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E4%BD%BF%E7%94%A8-Directory.Build.props-%E6%96%87%E4%BB%B6%E5%AE%9A%E4%B9%89%E7%BC%96%E8%AF%91.html&quot;&gt;Roslyn 使用 Directory.Build.props 文件定义编译 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-response-files&quot;&gt;使用 MSBuild 响应文件 (rsp) 来指定 dotnet build 命令行编译时的大量参数 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nuget-基础&quot;&gt;NuGet 基础&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以使用 NuGet 做一些不是传统 dll 引用的功能：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%80%9A%E8%BF%87-nuget-%E7%BB%9F%E4%B8%80%E7%AE%A1%E7%90%86%E4%BF%A1%E6%81%AF.html&quot;&gt;Roslyn 通过 nuget 统一管理信息 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%80%9A%E8%BF%87-Nuget-%E7%AE%A1%E7%90%86%E5%85%AC%E5%8F%B8%E9%85%8D%E7%BD%AE.html&quot;&gt;Roslyn 通过 Nuget 管理公司配置 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在，我们需要真的使用 NuGet 做一个自己的工具了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/include-dependencies-into-nuget-tool-package&quot;&gt;在制作跨平台的 NuGet 工具包时，如何将工具（exe/dll）的所有依赖一并放入包中 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NuGet 的坑很多，有些可以解，有些需要规避：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/prevent-nuget-package-been-depended&quot;&gt;帮助官方 NuGet 解掉 Bug，制作绝对不会传递依赖的 NuGet 包 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/problems-of-msbuild-and-nuget&quot;&gt;MSBuild/Roslyn 和 NuGet 的 100 个坑 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%80%9A%E8%BF%87-Nuget-%E5%BC%95%E7%94%A8%E6%BA%90%E4%BB%A3%E7%A0%81-%E5%9C%A8-VS-%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E6%AD%A3%E5%B8%B8%E4%BD%86%E6%98%AF%E6%97%A0%E6%B3%95%E7%BC%96%E8%AF%91.html&quot;&gt;Roslyn 通过 Nuget 引用源代码 在 VS 智能提示正常但是无法编译 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你的 NuGet 格式是旧的，或者说引用方式是旧的，推荐升级：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/migrate-packages-config-to-package-reference&quot;&gt;自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/migrate-nuget-package-from-powershell-to-props-and-targets&quot;&gt;如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference) - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;学会这些 NuGet 技能之后的一些应用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/prevent-nuget-package-upgrade&quot;&gt;阻止某个 NuGet 包意外升级 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sourceyard-原理&quot;&gt;SourceYard 原理&lt;/h2&gt;

&lt;p&gt;SourceYard 利用 NuGet 自动 Import 的 Target 来执行我们的代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E4%BD%BF%E7%94%A8-Target-%E6%9B%BF%E6%8D%A2%E5%8D%A0%E4%BD%8D%E7%AC%A6%E6%96%B9%E5%BC%8F%E7%94%9F%E6%88%90-nuget-%E6%89%93%E5%8C%85.html&quot;&gt;Roslyn 使用 Target 替换占位符方式生成 nuget 打包 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%80%9A%E8%BF%87-Target-%E4%BF%AE%E6%94%B9%E7%BC%96%E8%AF%91%E7%9A%84%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn 通过 Target 修改编译的文件 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-Microsoft.NET.Sdk-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85.html&quot;&gt;Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;roslyn-基础&quot;&gt;Roslyn 基础&lt;/h2&gt;

&lt;p&gt;Roslyn 由于其丰富且易用的 API，所以入门是比较容易的。推荐的入门文章有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90.html&quot;&gt;Roslyn 静态分析 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，你可以阅读更多 Roslyn 的资料以便快速应用于你的项目：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-tree-nodes&quot;&gt;Roslyn 语法树中的各种语法节点及每个节点的含义 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E8%8A%82%E7%82%B9%E7%9A%84-Span-%E5%92%8C-FullSpan-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB.html&quot;&gt;Roslyn 节点的 Span 和 FullSpan 有什么区别 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-NameSyntax-%E7%9A%84-ToString-%E5%92%8C-ToFullString-%E7%9A%84%E5%8C%BA%E5%88%AB.html&quot;&gt;Roslyn NameSyntax 的 ToString 和 ToFullString 的区别 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Roslyn 为何能够在提供如此友好的 API 的情况下依然有如此高的性能？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-red-green-tree-of-roslyn&quot;&gt;理解 Roslyn 中的红绿树（Red-Green Trees） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一些 Roslyn 的额外功能：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deterministic-builds-in-roslyn&quot;&gt;Roslyn 的确定性构建 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sourcefusion-预编译框架&quot;&gt;SourceFusion 预编译框架&lt;/h2&gt;

&lt;p&gt;关于预编译框架的博客没有那么多，只有一些基本的使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-your-own-extern-method&quot;&gt;都是用 DllImport？有没有考虑过自己写一个 extern 方法？ - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;扩展阅读&quot;&gt;扩展阅读&lt;/h2&gt;

&lt;p&gt;这里是是用到了 csproj / NuGet 等的额外博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/semantic-version&quot;&gt;语义版本号（Semantic Versioning） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-0&quot;&gt;(1/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-core-%E9%80%9A%E8%BF%87%E4%BF%AE%E6%94%B9%E6%96%87%E4%BB%B6%E5%A4%B4%E7%9A%84%E6%96%B9%E5%BC%8F%E9%9A%90%E8%97%8F%E6%8E%A7%E5%88%B6%E5%8F%B0%E7%AA%97%E5%8F%A3.html&quot;&gt;dotnet core 通过修改文件头的方式隐藏控制台窗口 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/automatically-semantic-versioning-using-git-version-task&quot;&gt;使用 GitVersion 在编译或持续构建时自动使用语义版本号（Semantic Versioning） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/automatically-semantic-versioning-using-git-version-task.en.html&quot;&gt;Automatically increase the semantic version using GitVersion - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:41:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/posts-for-learning-dotnet-build-nuget-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/posts-for-learning-dotnet-build-nuget-roslyn.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>(1/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序</title>
        <description>&lt;p&gt;每次使用 Visual Studio 的模板创建一个 UWP 程序，我们会在项目中发现大量的项目文件、配置、应用启动流程代码和界面代码。然而这些文件在 UWP 程序中到底是如何工作起来的？&lt;/p&gt;

&lt;p&gt;我从零开始创建了一个 UWP 程序，用于探索这些文件的用途，了解 UWP 程序的启动流程。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文分为两个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-0&quot;&gt;从零开始创建一个 UWP 项目并完成部署&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;从零开始编写一个 UWP 应用程序和窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文将一个普通项目改造成 UWP 项目，重点在了解 UWP 的项目文件组成。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;从最简单的项目模板开始&quot;&gt;从最简单的项目模板开始&lt;/h2&gt;

&lt;p&gt;虽然可以从零开始写一个 csproj 文件，不过这并没有什么技术含量，因为新的 csproj 文件实在是非常简单。参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，我创建一个 .NET Core 控制台应用。当然，其它简单的如 .NET Standard 库都是一样的，反正最后都会被我改得面目全非。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-20-37-18.png&quot; alt=&quot;创建 .NET Core 控制台应用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是我得到了一个 csproj 项目文件和包含有应用程序入口的 Program.cs 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-20-42-51.png&quot; alt=&quot;两个文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中 csproj 文件内容非常简单：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Program.cs 文件也是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// using System;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.ZeroUwp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这一句需要删除，因为 UWP 程序中不能使用控制台。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Console.WriteLine(&quot;Hello World!&quot;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，这两个文件都会被改掉的，已经无所谓里面是什么内容了。&lt;/p&gt;

&lt;h2 id=&quot;将项目改造成-uwp-项目&quot;&gt;将项目改造成 UWP 项目&lt;/h2&gt;

&lt;p&gt;UWP 程序的输出类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppContainerExe&lt;/code&gt;，而不是一般的 Library 或者 Exe。&lt;/p&gt;

&lt;p&gt;另外，基于 Sdk 风格的 csproj 格式不支持 UWP 应用程序。所以我希望借助第三方的 MSBuild.Sdk.Extras 来编译 UWP 的项目。参见 &lt;a href=&quot;/post/use-msbuild-sdk-extras-for-wpf-and-uwp&quot;&gt;Sdk 风格的 csproj 对 WPF/UWP 支持不太好？有第三方 SDK 可以用！MSBuild.Sdk.Extras&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;然而实际情况也不容乐观，因为此第三方 Sdk 只支持 UWP 的库程序，而不支持应用程序容器。所以即便修改为以下方式，最终也因为缺少 Visual Studio RunCommand 的支持，而导致无法启动。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild.Sdk.Extras/1.6.41&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;AppContainerExe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;uap10.0.17134&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用以上新 Sdk 的 csproj 格式，我完整地写完了整个 csproj 文件和后续步骤，依然无法解决下面这个错误提示框：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-08-31-09.png&quot; alt=&quot;RunCommand Property is not defined&quot; /&gt;&lt;br /&gt;
▲ 无法启动&lt;/p&gt;

&lt;p&gt;所以我们依然只能使用传统的 csproj 文件格式。里面大部分的内容从模板中复制而来。事实上，我寻找了很多资料，都没有找到让 Sdk 风格的 csproj 格式支持 UWP 的主程序。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DefaultTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Configuration&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Debug&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Platform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Platform)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;x86&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Platform&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectGuid&amp;gt;&lt;/span&gt;{09A58639-DC50-41C1-8BCE-A2217A1D3327}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectGuid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;AppContainerExe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppDesignerFolder&amp;gt;&lt;/span&gt;Properties&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppDesignerFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;Walterlv.Demo.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.Demo.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefaultLanguage&amp;gt;&lt;/span&gt;en-US&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefaultLanguage&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformIdentifier&amp;gt;&lt;/span&gt;UAP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformIdentifier&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(TargetPlatformVersion)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;10.0.17134.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformMinVersion&amp;gt;&lt;/span&gt;10.0.15063.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformMinVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MinimumVisualStudioVersion&amp;gt;&lt;/span&gt;14&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MinimumVisualStudioVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FileAlignment&amp;gt;&lt;/span&gt;512&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FileAlignment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectTypeGuids&amp;gt;&lt;/span&gt;{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectTypeGuids&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowsXamlEnableOverview&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowsXamlEnableOverview&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\x86\Debug\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;full&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;x86&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RestoreProjectStyle&amp;gt;&lt;/span&gt;PackageReference&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RestoreProjectStyle&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Program.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\AssemblyInfo.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppxManifest&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Package.appxmanifest&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;SubType&amp;gt;&lt;/span&gt;Designer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SubType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppxManifest&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NETCore.UniversalWindowsPlatform&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;6.1.5&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReference&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &amp;amp;lt; '14.0' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStudioVersion&amp;gt;&lt;/span&gt;14.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStudioVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写-appxmanifest&quot;&gt;编写 AppxManifest&lt;/h2&gt;

&lt;p&gt;项目改造成 UWP 项目后，似乎已经完成了大部分了，但此时直接运行会有编译错误，因为我缺少 UWP 程序必要的 AppxManifest.xml 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-20-58-01.png&quot; alt=&quot;缺少 AppxManifest.xml 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;事实上，AppxManifest.xml 的创建是非常繁琐的；通常是编译过程帮我们根据 Package.appxmanifest 文件自动生成的。然而创建一个 Package.appxmanifest 也是很麻烦的。至少，要让 Visual Studio 能够直接打开这个文件所需的最小代码量是下面这些（不能编译通过）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/foundation/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:mp=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/2014/phone/manifest&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap2=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10/2&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap3=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10/3&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:iot=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/iot/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:mobile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/mobile/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;IgnorableNamespaces=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;uap mp uap3 iot uap2 mobile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Identity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.zerouwp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Publisher=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CN=walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;mp:PhoneIdentity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PhoneProductId=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;97f5137d-c6be-4395-9af0-bbfdcea40fa7&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PhonePublisherId=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;00000000-0000-0000-0000-000000000000&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Properties&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DisplayName&amp;gt;&lt;/span&gt;Walterlv.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DisplayName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PublisherDisplayName&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PublisherDisplayName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Logo&amp;gt;&lt;/span&gt;Assets\StoreLogo.png&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Logo&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Properties&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Dependencies&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetDeviceFamily&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Windows.Universal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MinVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MaxVersionTested=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x-generate&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Resources&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Applications&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Executable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$targetnametoken$.exe&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EntryPoint=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ZeroUwp.Program&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;uap:VisualElements&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DisplayName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ZeroUwp&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/uap:VisualElements&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Applications&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以阅读这些文档了解如何完成这份文件的编写：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-identity?wt.mc_id=MVP&quot;&gt;Identity (Windows 10) - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-pm-phoneidentity?wt.mc_id=MVP&quot;&gt;pm:PhoneIdentity (Windows 10) - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Identity /&amp;gt;&lt;/code&gt; 是此程序包的标识符，需要在整个应用商店范围内唯一（如果将此包与应用商店关联，这个值会自动更新，所以不用在意填成什么）。&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;mp:PhoneIdentity /&amp;gt;&lt;/code&gt; 是此程序包在移动设备上的标识符，应用的更新会依据此标识符的 GUID 来唯一确定，格式必须是 GUID。&lt;/p&gt;

&lt;p&gt;事实上，虽然依然无法完成编译，但此时可以通过在 Visual Studio 中打开这份文件来观察还缺少哪些必要的信息需要填写。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-21-24-14.png&quot; alt=&quot;填写缺少的信息&quot; /&gt;&lt;/p&gt;

&lt;p&gt;事实上，我们缺少的信息并不多，只有四个，都从 Package/Applications/Application 开始：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;uap:VisualElements@Description&lt;/li&gt;
  &lt;li&gt;uap:VisualElements@BackgroundColor&lt;/li&gt;
  &lt;li&gt;uap:VisualElements@Square150x150Logo&lt;/li&gt;
  &lt;li&gt;uap:VisualElements@Square44x44Logo&lt;/li&gt;
  &lt;li&gt;uap:VisualElements/uap:DefaultTile@Wide310x150Logo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是 XPath 语法，详见：&lt;a href=&quot;/post/xml-xpath&quot;&gt;XML 的 XPath 语法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同时，我们还真的需要相应的图片：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-09-27-08.png&quot; alt=&quot;UWP 程序所需的最少 Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;建议从 UWP 程序模板中复制，也可以去这里下载：&lt;a href=&quot;https://download.csdn.net/download/wpwalter/10562268&quot;&gt;UWP 程序所需的最少 Logo 资源-CSDN下载&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;补充完毕之后，完整的文件如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/foundation/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:mp=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/2014/phone/manifest&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap2=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10/2&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:uap3=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/uap/windows10/3&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:iot=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/iot/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;xmlns:mobile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/appx/manifest/mobile/windows10&quot;&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;IgnorableNamespaces=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;uap mp uap3 iot uap2 mobile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Identity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.zerouwp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Publisher=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CN=walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Properties&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DisplayName&amp;gt;&lt;/span&gt;Walterlv.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DisplayName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PublisherDisplayName&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PublisherDisplayName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Logo&amp;gt;&lt;/span&gt;Assets\StoreLogo.png&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Logo&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Properties&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Dependencies&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetDeviceFamily&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Windows.Universal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MinVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MaxVersionTested=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x-generate&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Resources&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Applications&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Executable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$targetnametoken$.exe&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EntryPoint=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ZeroUwp.Program&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;uap:VisualElements&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DisplayName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ZeroUwp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Description=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ZeroUwp is a demo application to learn how uwp application runs.&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BackgroundColor=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;transparent&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Square150x150Logo=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square150x150Logo.png&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Square44x44Logo=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square44x44Logo.png&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;uap:DefaultTile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Wide310x150Logo=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Wide310x150Logo.png&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/uap:DefaultTile&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/uap:VisualElements&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Applications&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不能忘掉，这份文件还需要添加到 csproj 项目文件中：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 新增了此节点，即 AppxManifest 和相关资源。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppxManifest&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Package.appxmanifest&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;SubType&amp;gt;&lt;/span&gt;Designer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SubType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppxManifest&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square150x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square44x44Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\StoreLogo.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Wide310x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写-assemblyinfocs&quot;&gt;编写 AssemblyInfo.cs&lt;/h2&gt;

&lt;p&gt;由于没有新的基于 Sdk 的 csproj 文件支持，所以我们需要自己编写 AssemblyInfo.cs 文件，并放入到 Properties 文件夹中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.ZeroUwp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyProduct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.ZeroUwp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCopyright&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copyright © walterlv 2018&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ComVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后，csproj 文件会如下面这样。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DefaultTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Configuration&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Debug&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Platform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Platform)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;x86&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Platform&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectGuid&amp;gt;&lt;/span&gt;{7B81D14B-6094-44E1-9B2F-F577995A3CAF}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectGuid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;AppContainerExe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppDesignerFolder&amp;gt;&lt;/span&gt;Properties&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppDesignerFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;Walterlv.Demo.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.Demo.ZeroUwp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefaultLanguage&amp;gt;&lt;/span&gt;en-US&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefaultLanguage&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformIdentifier&amp;gt;&lt;/span&gt;UAP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformIdentifier&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(TargetPlatformVersion)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;10.0.17134.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetPlatformMinVersion&amp;gt;&lt;/span&gt;10.0.17134.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetPlatformMinVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MinimumVisualStudioVersion&amp;gt;&lt;/span&gt;14&lt;span class=&quot;nt&quot;&gt;&amp;lt;/MinimumVisualStudioVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FileAlignment&amp;gt;&lt;/span&gt;512&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FileAlignment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectTypeGuids&amp;gt;&lt;/span&gt;{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectTypeGuids&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowsXamlEnableOverview&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowsXamlEnableOverview&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Debug|x86'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\x86\Debug\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;full&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;x86&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Release|x86'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\x86\Release\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Optimize&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Optimize&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;pdbonly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;x86&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Debug|ARM'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\ARM\Debug\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;full&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;ARM&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Release|ARM'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\ARM\Release\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Optimize&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Optimize&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;pdbonly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;ARM&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Debug|x64'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\x64\Debug\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;full&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;x64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(Configuration)|$(Platform)' == 'Release|x64'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\x64\Release\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;TRACE;NETFX_CORE;WINDOWS_UWP&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Optimize&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Optimize&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;;2008&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;pdbonly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PlatformTarget&amp;gt;&lt;/span&gt;x64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PlatformTarget&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseVSHostingProcess&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseVSHostingProcess&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Prefer32Bit&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Prefer32Bit&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UseDotNetNativeToolchain&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RestoreProjectStyle&amp;gt;&lt;/span&gt;PackageReference&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RestoreProjectStyle&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\AssemblyInfo.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Program.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VisualProperties.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppxManifest&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Package.appxmanifest&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;SubType&amp;gt;&lt;/span&gt;Designer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SubType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppxManifest&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square150x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square44x44Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\StoreLogo.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Wide310x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NETCore.UniversalWindowsPlatform&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;6.1.5&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReference&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &amp;amp;lt; '14.0' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStudioVersion&amp;gt;&lt;/span&gt;14.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStudioVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个说明：如果运行时出现本机错误，那么可能是上面的 csproj 文件没有配置正确。如果出现下图所示的错误，建议先考虑将以上 csproj 文件中的所有内容复制到你的项目文件中再试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-09-28-42.png&quot; alt=&quot;本机错误&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;完成部署和运行&quot;&gt;完成部署和运行&lt;/h2&gt;

&lt;p&gt;以上所有内容是一个 UWP 程序完成编译并运行所需的最少信息了。&lt;/p&gt;

&lt;p&gt;此时运行，我们只会看到一个空的窗口，就像这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-08-57-17.png&quot; alt=&quot;空的窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Main 函数中的断点是可以进入的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-09-01-42.png&quot; alt=&quot;Main 函数中的断点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过，如果继续运行，会提示错误。因为我们的程序并没有显示任何 UWP 的界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-09-02-19.png&quot; alt=&quot;无法继续运行&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;总结与后续&quot;&gt;总结与后续&lt;/h2&gt;

&lt;p&gt;在本文中，我们了解到 UWP 项目所需的最少文件有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;*.csproj 项目文件
    &lt;ul&gt;
      &lt;li&gt;这是整个从零开始的 UWP 程序中最复杂的一个文件，因为目前没有找到任何一个 Sdk 支持 UWP 的主程序工程。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Package.appxmanifest 文件
    &lt;ul&gt;
      &lt;li&gt;这是 UWP 应用程序的清单文件。事实上，这不是最终的清单文件，而是用于在项目中填写信息的文件；从前面的错误信息中我们了解到，最终的清单文件是 AppxManifest.xml。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Assets 文件夹中的四张图片
    &lt;ul&gt;
      &lt;li&gt;StoreLogo、Square44x44Logo、Square150x150Logo 和 Wide310x150Logo 是清单文件能够正常生成所需的最少 Logo 资源&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;AssemblyInfo.cs
    &lt;ul&gt;
      &lt;li&gt;由于缺少 Project@Sdk 的支持，所以我们必须编写 AssemblyInfo.cs 文件来指定版本信息。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Program.cs
    &lt;ul&gt;
      &lt;li&gt;这是一开始我们就添加好的文件，就是放 Main 函数的地方。虽然我们什么都没写，但已经能够进入断点了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接下来我们将从 Main 函数开始，完成一个 UWP 程序的启动：&lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;(2/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:58 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-uwp-app-from-zero-0.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-uwp-app-from-zero-0.html</guid>
        
        
        <category>uwp</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>.NET/C# 项目如何优雅地设置条件编译符号？</title>
        <description>&lt;p&gt;条件编译符号指的是 Conditional Compilation Symbols。你可以在 Visual Studio 的项目属性中设置，也可以直接在项目文件中写入 &lt;code class=&quot;highlighter-rouge&quot;&gt;DefineConstants&lt;/code&gt; 属性。&lt;/p&gt;

&lt;p&gt;不过对于不同种类的项目，我建议使用不同的设置方法。本文将介绍如何设置条件编译符。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;对于新旧格式的差别或者迁移，可以查看我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;新格式推荐在-csproj-文件中设置&quot;&gt;新格式推荐：在 csproj 文件中设置&lt;/h2&gt;

&lt;p&gt;在项目中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;DefineConstants /&amp;gt;&lt;/code&gt; 属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp2.1;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;$(DefineConstants);WALTERLV&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我使用字符串拼接的方式 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(DefineConstants);WALTERLV&lt;/code&gt; 来设置，这样可以把预设的那些条件编译符号保留，比如通常 Visual Studio 会帮你生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TRACE&lt;/code&gt; 条件编译符。&lt;/p&gt;

&lt;p&gt;但即便你不做这种拼接也不用担心。因为基于框架或平台的条件编译符号是自动设置的。例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;NETCOREAPP2_1&lt;/code&gt; 等都是在你指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;DefineConstants&lt;/code&gt; 之后自动设置的。以下是 Microsoft.NET.Sdk 中的部分源码，可以证明这一点：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(DisableImplicitConfigurationDefines)' != 'true'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;$(Configuration.ToUpperInvariant())&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Replace dashes and periods in the configuration with underscores.  This makes it more likely that
       the resulting compilation constant will be a valid C# conditional compilation symbol.  As the set
       of characters that aren't allowed is essentially open-ended, there's probably not a good way to
       fully sanitize the Configuration in MSBuild evaluation.  If the resulting string still isn't a
       valid conditional combilation symbol, then the compiler will generate the following error and
       the define will be ignored:
          warning MSB3052: The parameter to the compiler is invalid, '/define:0BAD_DEFINE' will be ignored.
       --&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;$(ImplicitConfigurationDefine.Replace('-', '_'))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;$(ImplicitConfigurationDefine.Replace('.', '_'))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitConfigurationDefine&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;$(DefineConstants);$(ImplicitConfigurationDefine)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;$(DefineConstants);$(ImplicitFrameworkDefine)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;旧格式推荐在-visual-studio-项目属性中设置&quot;&gt;旧格式推荐：在 Visual Studio 项目属性中设置&lt;/h2&gt;

&lt;p&gt;你可以在项目属性的“生成”页中找到条件编译符号的设置。&lt;/p&gt;

&lt;p&gt;我自己用的 Visual Studio 是英文版的，但是也感谢小伙伴 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 帮我截了一张中文版的图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-24-21-34-59.png&quot; alt=&quot;Conditional Compilation Symbols&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-24-21-34-54.png&quot; alt=&quot;条件编译符号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你需要特别注意：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;设置条件编译符号需要在各种配置下都设置，因为各种配置都是不一样的；具体来说是 Debug 下要设，Release 下也要设，x86 下要设，x64 下也要设。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;关于配置configuration和条件编译符号conditional-compilation-symbols&quot;&gt;关于配置（Configuration）和条件编译符号（Conditional Compilation Symbols）&lt;/h2&gt;

&lt;p&gt;你可能在你的代码中同时看到 Pascal 命名规则的 Debug 和全部大写的 DEBUG，或者看到 Release 和 RELEASE。这是两个不同的概念。&lt;/p&gt;

&lt;p&gt;Debug 和 Release 的名称来自于配置（Configuration）。你的项目有 Debug 配置和 Release 配置，或者你自己定义的其他配置。你的项目编译过程默认根据 Debug 和 Release 配置做了很多不同的编译选项。例如 Debug 下会禁用优化而 Release 下会开启优化。&lt;/p&gt;

&lt;p&gt;而 DEBUG 和 RELEASE 这样的全大写名称来自于条件编译符号（Conditional Compilation Symbols），是真正在 C# 代码中使用的符号。而这全大写符号的定义是分别在 Debug 和 Release 配置下设置了不同的值来实现的。&lt;/p&gt;

&lt;p&gt;所以这两个是不同的概念，不要弄混淆了。&lt;/p&gt;

&lt;p&gt;同时这也带来了一些命名建议：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;条件编译符号使用全大写命名
    &lt;ul&gt;
      &lt;li&gt;例如：DEBUG, RELEASE, NET47, NETCOREAPP2_1&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;配置使用 Pascal 命名
    &lt;ul&gt;
      &lt;li&gt;例如：Debug, Release&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-define-preprocessor-symbols.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-define-preprocessor-symbols.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>.NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect)，解决不同版本 dll 的依赖问题</title>
        <description>&lt;p&gt;当你的项目中多个不同的项目以及不同的依赖存在不同的依赖程序集时，可能会因为依赖于不同版本的程序集而产生冲突。而绑定重定向可以帮助解决不同程序集的依赖版本不同的问题，使整个程序使用统一个版本的 dll 来运行整个应用程序。&lt;/p&gt;

&lt;p&gt;然而，如果我们就是需要使用一个分离的不同版本，那么我们就需要禁用掉自动生成绑定重定向。本文介绍如何禁用自动生成绑定重定向。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文的结论只有一句，就是在项目中设置属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;AutoGenerateBindingRedirects&amp;gt;false&amp;lt;/AutoGenerateBindingRedirects&amp;gt;&lt;/code&gt;。阅读本文全文是了解更多与绑定重定向此场景相关的知识。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;绑定重定向&quot;&gt;绑定重定向&lt;/h2&gt;

&lt;p&gt;从 .NET Framework 4.5.1 开始到后面的 .NET Core 所有版本，编译器会自动向你的程序集中插入绑定重定向。如果你升级使用了新的 csproj 格式，即便你用了旧的 .NET Framework 也会自动生成绑定重定向。&lt;/p&gt;

&lt;p&gt;关于新旧 csproj 格式，你可以参考我的另一篇博客：&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;你可以在你的应用程序的 App.config 文件中查看到自动生成的绑定重定向。当然，编译之后这个 App.config 文件会编程 “你的程序集名称.config” 文件，例如对于我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.exe&lt;/code&gt; 程序对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.exe.config&lt;/code&gt; 文件。&lt;/p&gt;

&lt;p&gt;一个典型的包含绑定重定向的文件大概是下面这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;startup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useLegacyV2RuntimeActivationPolicy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;supportedRuntime&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;sku=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework,Version=v4.6&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/startup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;runtime&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;assemblyBinding&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependentAssembly&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;assemblyIdentity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Newtonsoft.Json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;publicKeyToken=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30ad4fe6b2a6aeed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;culture=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;neutral&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;bindingRedirect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;oldVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.0.0.0-11.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;newVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;11.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependentAssembly&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependentAssembly&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;assemblyIdentity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;publicKeyToken=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cc7b13ffcd2ddd51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;culture=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;neutral&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;bindingRedirect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;oldVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.0.0.0-4.0.3.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;newVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0.3.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependentAssembly&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/assemblyBinding&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/runtime&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面 &lt;code class=&quot;highlighter-rouge&quot;&gt;dependentAssembly&lt;/code&gt; 以及 &lt;code class=&quot;highlighter-rouge&quot;&gt;bindingRedirect&lt;/code&gt; 就是在描述绑定重定向。&lt;/p&gt;

&lt;p&gt;对于上面的代码，指的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果依赖中发现了任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;0.0.0.0-11.0.0.0&lt;/code&gt; 区间版本号的 Newtonsoft.Json 程序集的引用，都将使用 11.0.0.0 版本的。&lt;/li&gt;
  &lt;li&gt;如果以来中发现了任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;0.0.0.0-4.0.3.0&lt;/code&gt; 区间版本号的 System.ValueTuple 程序集的引用，都将使用 4.0.3.0 版本的（这个其实使用的 NuGet 包版本是 4.5）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;引用同名但不同版本的-dll&quot;&gt;引用同名但不同版本的 dll&lt;/h2&gt;

&lt;p&gt;绑定重定向多数时候都是在帮助我们解决依赖问题，然而我们总有一些时候不是按照常规的方式来使用依赖，例如下文这样的方式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/dang13579/article/details/72956684&quot;&gt;引用不用版本dll - dang13579的专栏 - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jgrass.cc/2017-11-C-%E4%B8%AD%E5%BC%95%E7%94%A8%E4%B8%8D%E5%90%8C%E7%89%88%E6%9C%ACDLL/&quot;&gt;C# 引用不同版本dll - 晒太阳的猫&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/gudi/p/6958297.html&quot;&gt;同一个解决方案或有依赖关系的两个项目引用同名但不同版本的DLL - gudi - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上文章的场景，是需要在同一个解决方案的不同项目中引用不同版本的同名 dll。解决方法是像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependentAssembly&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;assemblyIdentity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;publicKeyToken=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4ee40123013c9f27&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;culture=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;neutral&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;codeBase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB.2.0.2.0\LiteDB.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;codeBase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB.4.0.0.0\LiteDB.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependentAssembly&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，如果引用了 2.0.2.0 版本的 LiteDB 的时候，会去应用程序所在目录的 LiteDB.2.0.2.0 子目录中查找名为 LiteDB.dll 的引用 dll；而如果引用了 4.0.0.0 版本的 LiteDB 的时候，会去应用程序所在目录的 LiteDB.4.0.0.0 子目录中查找名为 LiteDB.dll 的引用 dll。这种方式使用两个 dll 互不干扰。&lt;/p&gt;

&lt;h2 id=&quot;禁用绑定重定向&quot;&gt;禁用绑定重定向&lt;/h2&gt;

&lt;p&gt;如果你的项目从 .NET Framework 4.5 或者更早版本升级到 .NET Framework 4.5.1 或者 .NET Core 的版本，或者 csproj 的格式升级到了新的基于 Microsoft.NET.Sdk 的版本，那么绑定重定向就会从之前的手动编程自动生成。&lt;/p&gt;

&lt;p&gt;但是如果你编写了上一节中我们讲到的你需要引用同名程序集的多个版本的时候，如果依然自动生成绑定重定向，那么上面的功能会失效。&lt;/p&gt;

&lt;p&gt;解决方法，便是禁用自动生成绑定重定向。在你的主项目中添加一个属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;AutoGenerateBindingRedirects&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AutoGenerateBindingRedirects&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection&quot;&gt;Enable or disable autogenerated binding redirects - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/disable-generating-binding-redirects.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/disable-generating-binding-redirects.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>如何更精准地设置 C# / .NET Core 项目的输出路径？（包括添加和删除各种前后缀）</title>
        <description>&lt;p&gt;我们都知道可以通过在 Visual Studio 中设置输出路径（OutputPath）来更改项目输出文件所在的位置。对于 .NET Core 所使用的 Sdk 风格的 csproj 格式来说，你可能会发现实际生成路径中带了 &lt;code class=&quot;highlighter-rouge&quot;&gt;netcoreapp3.0&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;net472&lt;/code&gt; 这样的子文件夹。&lt;/p&gt;

&lt;p&gt;然而有时我们并不允许生成这样的子文件夹。本文将介绍可能影响实际输出路径的各种设置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;项目和输出路径&quot;&gt;项目和输出路径&lt;/h2&gt;

&lt;p&gt;对于这样的一个简单的项目文件，这个项目的实际输出路径可能是像下图那样的。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\$(Configuration)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-21-17-54-13.png&quot; alt=&quot;输出路径带有框架子文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有没有办法可以不要生成这样的子文件夹呢？答案是可以的。&lt;/p&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程&lt;/a&gt; 一文中有说到如何解读 Microsoft.NET.Sdk，而我们的答案就是从解读这个 Sdk 而来。&lt;/p&gt;

&lt;h2 id=&quot;影响输出路径的属性&quot;&gt;影响输出路径的属性&lt;/h2&gt;

&lt;p&gt;OutputPath 属性由这些部分组成：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(BaseOutputPath)\$(PlatformName)\$(Configuration)\$(RuntimeIdentifier)\$(TargetFramework.ToLowerInvariant())\
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果以上所有属性都有值，那么生成的路径可能就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin\x64\Debug\win7-x64\netcoreapp3.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体的，这些属性以及其相关的设置有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseOutputPath)&lt;/code&gt; 默认值 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\&lt;/code&gt;，你也可以修改。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PlatformName)&lt;/code&gt; 默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(Platform)&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(Platform)&lt;/code&gt; 的默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnyCPU&lt;/code&gt;；当这个值等于 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnyCPU&lt;/code&gt; 的时候，这个值就不会出现在路径中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Configuration)&lt;/code&gt; 默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RuntimeIdentifier)&lt;/code&gt; 这个值和 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(PlatformTarget)&lt;/code&gt; 互为默认值，任何一个先设置都会影响另一个；此值即 &lt;code class=&quot;highlighter-rouge&quot;&gt;x86&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;x64&lt;/code&gt; 等标识符。可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(AppendRuntimeIdentifierToOutputPath)&lt;/code&gt; 属性指定是否将此加入到输出路径中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(TargetFramework)&lt;/code&gt; 这是在 csproj 文件中强制要求指定的，如果不设置的话项目是无法编译的；可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(AppendTargetFrameworkToOutputPath)&lt;/code&gt; 属性指定是否将此加入到输出路径中。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在，你应该可以更轻松地设置你的输出路径，而不用担心总会出现各种意料之外的子文件夹了吧！&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-properties-that-affetcs-project-output-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-properties-that-affetcs-project-output-path.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 新旧不同的 csproj 项目格式中启用混合模式调试程序（开启本机代码调试）</title>
        <description>&lt;p&gt;因为我使用 Visual Studio 主要用来编写 .NET 托管程序，所以平时调试的时候是仅限托管代码的。不过有时需要在托管代码中混合调试本机代码，那么就需要额外在项目中开启本机代码调试。&lt;/p&gt;

&lt;p&gt;本文介绍如何开启本机代码调试。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;本文涉及到新旧 csproj 项目格式，不懂这个也不影响你完成开启本机代码调试。不过如果你希望了解，可以阅读：&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;在旧格式的项目中开启&quot;&gt;在旧格式的项目中开启&lt;/h2&gt;

&lt;p&gt;旧格式指的是 Visual Studio 2015 及以前版本的 Visual Studio 使用的项目格式。目前 Visual Studio 2017 和 2019 对这种格式的支持还是很完善的。&lt;/p&gt;

&lt;p&gt;在项目上右键 -&amp;gt; 属性 -&amp;gt; Debug，这时你可以在底部的调试引擎中发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enable native code debugging&lt;/code&gt; 选项，开启它你就开启了本机代码调试，于是也就可以使用混合模式调试程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-25-48.png&quot; alt=&quot;在旧格式中开启本机代码调试&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;在新格式的项目中开启&quot;&gt;在新格式的项目中开启&lt;/h2&gt;

&lt;p&gt;如果你在你项目属性的 Debug 标签下没有找到上面那个选项，那么有可能你的项目格式是新格式的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-31-27.png&quot; alt=&quot;新格式中没有开启本机代码调试的选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个时候，你需要在 &lt;em&gt;lauchsettings.json&lt;/em&gt; 文件中设置。这个文件在你项目的 Properties 文件夹下。&lt;/p&gt;

&lt;p&gt;如果你没有找到这个文件，那么随便在上图那个框框中写点什么（比如在启动参数一栏中写 &lt;em&gt;吕毅是逗比&lt;/em&gt;），然后保存。我们就能得到一个 &lt;em&gt;lauchsettings.json&lt;/em&gt; 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-34-23.png&quot; alt=&quot;launchsettings.json 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开它，然后删掉刚刚的逗比行为，添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;nativeDebugging&quot;: true&lt;/code&gt;。这时，你的 &lt;em&gt;lauchsettings.json&lt;/em&gt;  文件影响像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;profiles&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Walterlv.Debugging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Project&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nativeDebugging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时你就可以开启本机代码调试了。当然，新的项目格式支持设置多个这样的启动项，于是你可以分别配置本机和非本机的多种配置：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;profiles&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Walterlv.Debugging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Project&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;本机调试&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Project&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nativeDebugging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，你可以选择你项目的启动方式了，其中一个是开启了本机代码调试的方式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-38-55.png&quot; alt=&quot;选择项目的启动方式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于这些配置的更多博客，你可以阅读：&lt;a href=&quot;https://lindexi.gitee.io/post/VisualStudio-%E4%BD%BF%E7%94%A8%E5%A4%9A%E4%B8%AA%E7%8E%AF%E5%A2%83%E8%BF%9B%E8%A1%8C%E8%B0%83%E8%AF%95.html&quot;&gt;VisualStudio 使用多个环境进行调试 - 林德熙&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-debug-in-mixed-mode?view=vs-2017&quot;&gt;How to: Debug in Mixed Mode - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-debug-managed-and-native-code?view=vs-2017&quot;&gt;Tutorial: Debug C# and C++ code (mixed mode) - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/visual-studio-enable-native-code-debugging.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/visual-studio-enable-native-code-debugging.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Visual Studio 2017 以前的旧格式的 csproj Import 进来的 targets 文件有时不能正确计算属性（PropertyGroup）和集合（ItemGroup）</title>
        <description>&lt;p&gt;我在之前的博客中有教大家如何编写 NuGet 工具包，其中就有编写 .targets 文件。&lt;/p&gt;

&lt;p&gt;我在实际的使用中，发现 Visual Studio 2017 带来的 Sdk 风格的 csproj 格式基本上没有多少坑；然而旧的 csproj 文件却总是不能完美的运行，总是出错。关键是，不是每台电脑都出错，不是每个时机都出错。&lt;/p&gt;

&lt;p&gt;本文将讲一些坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;本文的前置知识&quot;&gt;本文的前置知识&lt;/h2&gt;

&lt;p&gt;你可能需要了解 csproj 文件的格式和编译过程，才可能读懂本文，所以需要先阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;下面的代码来自 &lt;a href=&quot;https://github.com/dotnet-campus/SourceFusion&quot;&gt;SourceFusion&lt;/a&gt; 项目的早期版本。&lt;/p&gt;

&lt;p&gt;这是一个 .targets 文件，项目安装此 NuGet 包之后就会自动 Import 这个 targets 文件。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_DefaultSourceFusionWorkingFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(_DefaultSourceFusionWorkingFolder)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;obj\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_DefaultSourceFusionWorkingFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionWorkingFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(SourceFusionWorkingFolder)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(_DefaultSourceFusionWorkingFolder)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionWorkingFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionToolsFolder&amp;gt;&lt;/span&gt;$(SourceFusionWorkingFolder)SourceFusion.Tools\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionToolsFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionGeneratedCodeFolder&amp;gt;&lt;/span&gt;$(SourceFusionWorkingFolder)SourceFusion.GeneratedCodes\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionGeneratedCodeFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_SourceFusionCreateDirectories&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_SourceFusionWriteCompilingArgs;_SourceFusionWriteFilterArgs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionDirectory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(SourceFusionWorkingFolder)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionDirectory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(SourceFusionToolsFolder)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionDirectory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(SourceFusionGeneratedCodeFolder)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;MakeDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(SourceFusionDirectory)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ContinueOnError=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;代码的解读如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建了一个私有属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;_DefaultSourceFusionWorkingFolder&lt;/code&gt;，三个公有属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionWorkingFolder&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionToolsFolder&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionGeneratedCodeFolder&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;在编译期间，执行一个私有的 Target，收集所有收集到的文件夹，形成一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionDirectory&lt;/code&gt; 集合。然后将集合中的所有字符串视为文件夹，创建这几个文件夹。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在新的有 Sdk 的 csproj 中，这个 targets 文件的执行没有问题。但是，对于旧的 csproj 来说，就经常出现这几个属性为空或者部分为空的情况。额外的，就算修改这个文件，上面的属性也不会生效。&lt;/p&gt;

&lt;p&gt;不过，如果使用命令行进行编译，这个却又是生效的。&lt;/p&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;究其原因，这是 MSBuild 对项目文件（csproj）的解析和 Visual Studio 对项目文件的解析是不同的。命令行使用的是 &lt;a href=&quot;https://github.com/Microsoft/msbuild&quot;&gt;MSBuild&lt;/a&gt; 解析 csproj，而 Visual Studio 使用的是 &lt;a href=&quot;https://github.com/Microsoft/VSProjectSystem&quot;&gt;VSProjectSystem&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;对于 VSProjectSystem 来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 根节点下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 对不会更新。有时清除 Visual Studio 的项目缓存可以解决这个问题，但有时清除也不能解决。&lt;/p&gt;

&lt;p&gt;真实的原因我并没有调查出来。但以上代码在大多数开发者的 Visual Studio 中是可以正常使用的，但有少数开发者使用这个会出现错误（没有创建任何文件夹）。&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;既然问题出在 &lt;a href=&quot;https://github.com/Microsoft/msbuild&quot;&gt;MSBuild&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/Microsoft/VSProjectSystem&quot;&gt;VSProjectSystem&lt;/a&gt; 对属性和集合处理的不同，那么我就不要创建动态的集合，而是在 Target 内部编写属性和集合。&lt;/p&gt;

&lt;p&gt;在 Target 内部的属性和集合将在编译期间进行计算，而不是在 Visual Studio 打开的时候就计算好。于是我们每次编译的时候都可以获得最新的属性和集合的值。&lt;/p&gt;

&lt;h2 id=&quot;衍生知识&quot;&gt;衍生知识&lt;/h2&gt;

&lt;p&gt;旧格式的 csproj 是不会自动计算属性和集合的变更的，这也是为什么项目文件改变的时候，Visual Studio 需要重新加载项目才可以正常显示和编译项目。同时，如果编辑旧格式的 csproj 文件，也需要先卸载掉项目才可以。而新格式的 csproj 是可以直接编辑而不需要卸载项目的，同时如果被外部改变，也不需要重新加载项目，而是可以直接计算出来新的属性和集合。&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:40:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/properties-not-correct-in-targets-file-imported-to-old-csproj-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/properties-not-correct-in-targets-file-imported-to-old-csproj-file.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的？</title>
        <description>&lt;p&gt;当我们在传统格式的 csproj 项目文件中安装 NuGet 包后，有时会在项目文件中发现空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 节点。这个空的节点让我们这波强迫症患者觉得有点难以接受，关键是手工删除之后也没发现有什么副作用。&lt;/p&gt;

&lt;p&gt;那么为什么会出现这个节点？它究竟有什么作用？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;空的-nugetpackageimportstamp-节点&quot;&gt;空的 NuGetPackageImportStamp 节点&lt;/h2&gt;

&lt;p&gt;NuGetPackageImportStamp 节点只会出现在传统的 csproj 文件中。如果你不清楚我这里指的传统的和新的 csproj 文件格式，那么可以阅读我的另一篇文章来了了解它们的区别：&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;简单说来，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 根节点中可以指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 特性的 csproj 文件格式是新的 csproj 格式。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 特性的存在，使得很多的项目文件的功能得以有一个默认的实现。&lt;/p&gt;

&lt;p&gt;而传统的 csproj 由于没有指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 特性，所以很多的特性如果需要执行，需要先 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 到 csproj 中，或者不断地修改 csproj 文件的内容以添加新的功能。&lt;/p&gt;

&lt;p&gt;空的 NuGetPackageImportStamp 节点只会出现在传统的 csproj 文件中。如果你使用新格式的 csproj 文件，那么无论你如何安装 NuGet 包，都是不会看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 节点出现的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 在传统 csproj 文件中是这样的：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;Project ToolsVersion=&quot;15.0&quot; xmlns=&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;NuGetPackageImportStamp&amp;gt;
++      &amp;lt;/NuGetPackageImportStamp&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;文件已经经过过度简化，肯定是编译不过的了。不过，你可以意会。它会在某些 NuGet 包安装完后出现在 csproj 文件中。&lt;/p&gt;

&lt;h2 id=&quot;什么情况下会出现-nugetpackageimportstamp-节点&quot;&gt;什么情况下会出现 NuGetPackageImportStamp 节点&lt;/h2&gt;

&lt;p&gt;你也许会发现，并不是所有的 NuGet 包安装完后都会出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 节点。实际上，只有那些会导致新 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 文件部件的 NuGet 包才会出现这样的节点。&lt;/p&gt;

&lt;p&gt;我们来了做个实验。&lt;/p&gt;

&lt;h3 id=&quot;不会新增-nugetpackageimportstamp&quot;&gt;不会新增 NuGetPackageImportStamp&lt;/h3&gt;

&lt;p&gt;在项目中安装 &lt;a href=&quot;https://www.nuget.org/packages/Newtonsoft.Json&quot;&gt;Newtonsoft.Json&lt;/a&gt;。安装完后，你会看到仓库中有两个文件发生了变化：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-25-15-52-50.png&quot; alt=&quot;两个文件发生了变化&quot; /&gt;&lt;br /&gt;
▲ 两个文件发生了变化&lt;/p&gt;

&lt;p&gt;一个是 packages.config 文件，这是传统的 NuGet 包管理方式所需要的一个文件，用于记录当前项目中管理的 NuGet 包信息。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;packages&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;package id=&quot;Newtonsoft.Json&quot; version=&quot;11.0.2&quot; targetFramework=&quot;net473&quot; /&amp;gt;
&lt;/span&gt;    &amp;lt;/packages&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另一个是 csproj 文件：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;Project ToolsVersion=&quot;15.0&quot; xmlns=&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&amp;gt;
      &amp;lt;ItemGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;Reference Include=&quot;Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL&quot;&amp;gt;
++        &amp;lt;HintPath&amp;gt;..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll&amp;lt;/HintPath&amp;gt;
++      &amp;lt;/Reference&amp;gt;
&lt;/span&gt;        &amp;lt;Reference Include=&quot;System&quot; /&amp;gt;
      &amp;lt;ItemGroup&amp;gt;
    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们发现，安装 &lt;a href=&quot;https://www.nuget.org/packages/Newtonsoft.Json&quot;&gt;Newtonsoft.Json&lt;/a&gt; 是不会导致项目中新增 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 节点的。&lt;/p&gt;

&lt;h3 id=&quot;会新增-nugetpackageimportstamp&quot;&gt;会新增 NuGetPackageImportStamp&lt;/h3&gt;

&lt;p&gt;现在，我们换另一个 NuGet 包来安装：&lt;a href=&quot;https://www.nuget.org/packages/StyleCop.MSBuild&quot;&gt;StyleCop.MSBuild&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;同样是两个文件的变化，一个是 packages.config 文件。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;packages&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;package id=&quot;StyleCop.MSBuild&quot; version=&quot;5.0.0&quot; targetFramework=&quot;net471&quot; developmentDependency=&quot;true&quot; /&amp;gt;
&lt;/span&gt;    &amp;lt;/packages&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另一个是 csproj 文件：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;Project ToolsVersion=&quot;15.0&quot; xmlns=&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&amp;gt;
      &amp;lt;Import Project=&quot;..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets&quot; Condition=&quot;Exists('..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')&quot; /&amp;gt;
      &amp;lt;PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++      &amp;lt;NuGetPackageImportStamp&amp;gt;
++      &amp;lt;/NuGetPackageImportStamp&amp;gt;
&lt;/span&gt;      &amp;lt;/PropertyGroup&amp;gt;
&lt;span class=&quot;gi&quot;&gt;++    &amp;lt;Target Name=&quot;EnsureNuGetPackageBuildImports&quot; BeforeTargets=&quot;PrepareForBuild&quot;&amp;gt;
++      &amp;lt;PropertyGroup&amp;gt;
++        &amp;lt;ErrorText&amp;gt;This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.&amp;lt;/ErrorText&amp;gt;
++      &amp;lt;/PropertyGroup&amp;gt;
++      &amp;lt;Error Condition=&quot;!Exists('..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')&quot; Text=&quot;$([System.String]::Format('$(ErrorText)', '..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets'))&quot; /&amp;gt;
++    &amp;lt;/Target&amp;gt;
&lt;/span&gt;    &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们发现，安装此 &lt;a href=&quot;https://www.nuget.org/packages/StyleCop.MSBuild&quot;&gt;StyleCop.MSBuild&lt;/a&gt; NuGet 包的情况下，csproj 文件中新增了两个大的内容块：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;用于 Import 一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;targets&lt;/code&gt; 文件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;nugetpackageimportstamp-的出现目的&quot;&gt;NuGetPackageImportStamp 的出现目的&lt;/h2&gt;

&lt;p&gt;我们发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGetPackageImportStamp&lt;/code&gt; 其实是伴随着 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 而出现的。而微软官方的注释也是诡异地说出了它的原因：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The overrides should ensure that Sets NuGetPackageImportStamp to a new random guid.&lt;br /&gt;
This is a hack to let the project system know it is out of date.&lt;br /&gt;
The value does not matter, it just needs to change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是为了让 Visual Studio 运行的时候，能够检测到 csproj 文件改变，以便重新加载这个项目，因为需要 Import 新的内容。在以前的 Visual Studio 版本中，会随机写下一段字符串；在新的版本中，它是个空字符串。&lt;/p&gt;

&lt;p&gt;由于新的 csproj 文件能够识别到外部 Import 文件的改变，所以其实并不需要这样的机制来让 Visual Studio 感知到文件的改变。&lt;/p&gt;

&lt;p&gt;在 Visual Studio 2017（工具版本 15.0）中，这个值会设为空，而在较低版本（14.0 及以下）这个值会设为一个随机的 guid。&lt;/p&gt;

&lt;p&gt;以下是 NuGet 客户端设置此值的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// This method should be on the UI thread. The overrides should ensure that&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Sets NuGetPackageImportStamp to a new random guid. This is a hack to let the project system know it is out&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// of date.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// The value does not matter, it just needs to change.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateImportStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IVsProjectAdapter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vsProjectAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ThreadHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowIfNotOnUIThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propStore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vsProjectAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VsHierarchy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IVsBuildPropertyStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propStore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// &amp;lt;NuGetPackageImportStamp&amp;gt;af617720&amp;lt;/NuGetPackageImportStamp&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NewGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'-'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;propStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetPropertyValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetImportStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_PersistStorageType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PST_PROJECT_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExceptionHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteErrorToActivityLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Remove the NuGetImportStamp so that VC++ project file won't be updated with this stamp on disk,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// which causes unnecessary source control pending changes.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;propStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NuGetImportStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_PersistStorageType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PST_PROJECT_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExceptionHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteErrorToActivityLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:39:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-empty-nuget-package-import-stamp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-empty-nuget-package-import-stamp.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>.NET/C# 中你可以在代码中写多个 Main 函数，然后按需要随时切换</title>
        <description>&lt;p&gt;.NET/C# 程序从 Main 函数开始执行，基本上各种书籍资料都是这么写的。不过，我们可以写多个 Main 函数，然后在项目文件中设置应该选择哪一个 Main 函数。&lt;/p&gt;

&lt;p&gt;你可能会觉得这样没有什么用，不过如果你的应用程序在不同的编译条件下有不同的启动代码，或者你需要持续去大范围修改启动代码，那么做一个 Main 函数的选择器是一个不错的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;在哪里选择-main&quot;&gt;在哪里选择 Main？&lt;/h2&gt;

&lt;p&gt;在带有 Main 函数的项目上 “右键 -&amp;gt; 属性 -&amp;gt; 应用 -&amp;gt; 启动对象”，可以看到我们的 Main 函数，默认值是 “未设置”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-17-54-01.png&quot; alt=&quot;选择 Main 函数&quot; /&gt;&lt;br /&gt;
▲ 选择 Main 函数&lt;/p&gt;

&lt;p&gt;在我们保持这个值没有设置的情况下，如果写两个 Main 函数，那么就会出现编译错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-18-00-40.png&quot; alt=&quot;两个 Main 函数&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error CS0017
Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point.
Walterlv.Demo.Main C:\Users\lvyi\Desktop\Walterlv.Demo.Main\Walterlv.Demo.Main\NewProgram.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，从两个 Main 函数中选择一个就好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-18-18-25.png&quot; alt=&quot;选择一个 Main 函数&quot; /&gt;&lt;br /&gt;
▲ 选择一个 Main 函数&lt;/p&gt;

&lt;h2 id=&quot;我们准备一个-wpf-程序&quot;&gt;我们准备一个 WPF 程序&lt;/h2&gt;

&lt;p&gt;现在，我们来一些更复杂的操作。现在把我们的项目换成一个普通的 WPF 项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-20-21-49.png&quot; alt=&quot;普通 WPF 项目&quot; /&gt;&lt;br /&gt;
▲ 普通 WPF 项目&lt;/p&gt;

&lt;p&gt;把启动对象换成 Walterlv.Demo.App：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-19-52-46.png&quot; alt=&quot;更换启动对象为&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，我们可以启动我们的 WPF 项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-19-54-08.png&quot; alt=&quot;新启动的 WPF 程序&quot; /&gt;&lt;br /&gt;
▲ 新启动的 WPF 程序&lt;/p&gt;

&lt;p&gt;这是个 Demo 程序，代码比较简单。值得注意的是，如果使用新的 csproj 文件，其内容如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LanguageTargets&amp;gt;&lt;/span&gt;$(MSBuildToolsPath)\Microsoft.CSharp.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LanguageTargets&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;Walterlv.Demo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StartupObject&amp;gt;&lt;/span&gt;Walterlv.Demo.App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StartupObject&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PresentationCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PresentationFramework&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Xaml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowsBase&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ApplicationDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Designer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild:Compile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Designer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild:Compile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DependentUpon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(Filename)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以通过阅读 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt; 完成这样的新旧格式迁移。&lt;/p&gt;

&lt;p&gt;App.xaml 中保持默认的代码即可：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.App&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;App.xaml.cs 中的代码比较简单，就是启动一个 MainWindow：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，我们的 Program 和 NewProgram 还是保持之前的代码不变，因为我们的启动对象已经被设置为了 Walterlv.Demo.App，所以这里的两个 Main 函数其实并没有起作用。&lt;/p&gt;

&lt;h2 id=&quot;根据启动对象的不同控制不同的启动流程&quot;&gt;根据启动对象的不同，控制不同的启动流程&lt;/h2&gt;

&lt;p&gt;现在，我们即将实现一个功能：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当在属性页中切换启动对象的时候，我们的启动流也能跟着改变。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体来说，我们的 Program 启动一个 App，而 NewProgram 启动另一个 App。&lt;/p&gt;

&lt;p&gt;于是，我们在 App.xaml.cs  之外再新建一个 App.new.xaml.cs。这两个 App 类可以共用一个 App.xaml 文件。&lt;/p&gt;

&lt;p&gt;于是我们需要修改 csproj 的代码（以下红色表示删除的行，绿色表示新增的行）：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

    &amp;lt;PropertyGroup&amp;gt;
      &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
      &amp;lt;TargetFramework&amp;gt;net472&amp;lt;/TargetFramework&amp;gt;
      &amp;lt;LanguageTargets&amp;gt;$(MSBuildToolsPath)\Microsoft.CSharp.targets&amp;lt;/LanguageTargets&amp;gt;
      &amp;lt;RootNamespace&amp;gt;Walterlv.Demo&amp;lt;/RootNamespace&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-     &amp;lt;StartupObject&amp;gt;Walterlv.Demo.App&amp;lt;/StartupObject&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;StartupObject&amp;gt;Walterlv.Demo.NewProgram&amp;lt;/StartupObject&amp;gt;
&lt;/span&gt;    &amp;lt;/PropertyGroup&amp;gt;

+   &amp;lt;PropertyGroup Condition=&quot; '$(StartupObject)' == 'Walterlv.Demo.Program' &quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;!-- 启用原启动流中的 App.xaml.cs 文件 --&amp;gt;
+     &amp;lt;AppCsPath&amp;gt;App.xaml.cs&amp;lt;/AppCsPath&amp;gt;
+   &amp;lt;/PropertyGroup&amp;gt;
+   &amp;lt;PropertyGroup Condition=&quot; '$(StartupObject)' == 'Walterlv.Demo.NewProgram' &quot;&amp;gt;
+     &amp;lt;!-- 启用新启动流中的 App.xaml.cs 文件 --&amp;gt;
+     &amp;lt;AppCsPath&amp;gt;App.new.xaml.cs&amp;lt;/AppCsPath&amp;gt;
+   &amp;lt;/PropertyGroup&amp;gt;
+
&lt;/span&gt;    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;Reference Include=&quot;PresentationCore&quot; /&amp;gt;
      &amp;lt;Reference Include=&quot;PresentationFramework&quot; /&amp;gt;
      &amp;lt;Reference Include=&quot;System.Xaml&quot; /&amp;gt;
      &amp;lt;Reference Include=&quot;WindowsBase&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
      &amp;lt;ApplicationDefinition Include=&quot;App.xaml&quot; SubType=&quot;Designer&quot; Generator=&quot;MSBuild:Compile&quot; /&amp;gt;
      &amp;lt;Page Include=&quot;**\*.xaml&quot; Exclude=&quot;App.xaml&quot; SubType=&quot;Designer&quot; Generator=&quot;MSBuild:Compile&quot; /&amp;gt;
      &amp;lt;Compile Update=&quot;**\*.xaml.cs&quot; DependentUpon=&quot;%(Filename)&quot; /&amp;gt;

+     &amp;lt;!-- 删掉两个 App.xaml.cs 文件，以便后面可以重新添加 --&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;Compile Remove=&quot;App.xaml.cs&quot; /&amp;gt;
+     &amp;lt;Compile Remove=&quot;App.new.xaml.cs&quot; /&amp;gt;
+     &amp;lt;Compile Include=&quot;$(AppCsPath)&quot; DependentUpon=&quot;App.xaml&quot; SubType=&quot;Designer&quot; /&amp;gt;
&lt;/span&gt;
    &amp;lt;/ItemGroup&amp;gt;

  &amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;增加的判断其实是根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(StartupObject)&lt;/code&gt; 值的不同，设置不同的 App.xaml.cs 文件与 App.xaml 文件对应。于是，我们也可以有不同的 App.xaml.cs 文件了。&lt;/p&gt;

&lt;p&gt;比如我们的 App.new.xaml.cs 文件中的内容就与 App.xaml.cs 中的不一样。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MainWindow&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;New Walterlv Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在新的文件中，我们修改了窗口的标题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-20-19-57.png&quot; alt=&quot;新设置的窗口标题&quot; /&gt;&lt;br /&gt;
▲ 新设置的窗口标题&lt;/p&gt;

&lt;p&gt;通过切换启动对象，我们的解决方案窗格中也能显示不同的 App.xaml.cs 文件。（不过需要提醒，可能需要卸载然后重新加载项目才会看到修改；否则只是能够编译通过，但看不见文件。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-09-20-15-55.png&quot; alt=&quot;可以看得见两个文件的切换&quot; /&gt;&lt;br /&gt;
▲ 可以看得见两个文件的切换&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;window&lt;/code&gt; 是局部变量，所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数中是不能修改到的。而采用了这种根据启动对象不同动态改变 App.xaml.cs 的方式解决了这个问题。&lt;/p&gt;

&lt;h2 id=&quot;将不同的文件换成不同的条件编译符&quot;&gt;将不同的文件换成不同的条件编译符&lt;/h2&gt;

&lt;p&gt;如果你的启动流程差异并不是那么大，那么也可以使用条件编译符的定义来替代整个文件的替换。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;PropertyGroup Condition=&quot; '$(StartupObject)' == 'Walterlv.Demo.Program' &quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-   &amp;lt;AppCsPath&amp;gt;App.xaml.cs&amp;lt;/AppCsPath&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;DefineConstants&amp;gt;$(DefineConstants);OLD&amp;lt;/DefineConstants&amp;gt;
&lt;/span&gt;  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;PropertyGroup Condition=&quot; '$(StartupObject)' == 'Walterlv.Demo.NewProgram' &quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-   &amp;lt;AppCsPath&amp;gt;App.new.xaml.cs&amp;lt;/AppCsPath&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;DefineConstants&amp;gt;$(DefineConstants);NEW&amp;lt;/DefineConstants&amp;gt;
&lt;/span&gt;  &amp;lt;/PropertyGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，可以通过条件编译符来控制新旧启动代码：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    using System.Windows;

    namespace Walterlv.Demo
    {
        public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                var window = new MainWindow()
&lt;span class=&quot;gi&quot;&gt;+   #if NEW
&lt;/span&gt;                {
                    Title = &quot;New Walterlv Demo&quot;,
                };
&lt;span class=&quot;gi&quot;&gt;+   #endif
&lt;/span&gt;                window.Show();

                base.OnStartup(e);
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:39:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-multiple-main-and-related-startup-codes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-multiple-main-and-related-startup-codes.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>MSBuild/Roslyn 和 NuGet 的 100 个坑</title>
        <description>&lt;p&gt;MSBuild 不愧是强大的编译器，它提供的扩展机制让你几乎可以编译任何类型的文件或项目；Roslyn 是全新编写的一套编译器，不过它保留了 MSBuild 的大部分机制；NuGet 是 .NET 生态系统中的包管理机制，被原生集成在新的 Microsoft.NET.Sdk 中。&lt;/p&gt;

&lt;p&gt;不过，他们的坑还是挺多的；本文就是他们 100 个坑的集合。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;系列博客&quot;&gt;系列博客&lt;/h2&gt;

&lt;p&gt;这是兄弟篇中的一篇，关于 MSBuild/Roslyn 和 NuGet 的 100 个坑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/problems-of-msbuild-and-nuget&quot;&gt;MSBuild/Roslyn 和 NuGet 的 100 个坑&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于这篇博客是大量坑的记录，所以是它建立在你已经对 MSBuild/Roslyn 和 NuGet 有一些了解的基础之上的。我摘取了一些入门系列文章，也许你可以通过阅读这些来了解下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;迁移 csproj 文件到基于 Microsoft.NET.Sdk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;创建基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;创建基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然还有更多，可以访问 &lt;a href=&quot;https://walterlv.github.io/categories#nuget&quot;&gt;https://walterlv.github.io/categories#nuget&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;100-个坑&quot;&gt;100 个坑&lt;/h2&gt;

&lt;h3 id=&quot;不可用的源&quot;&gt;不可用的源&lt;/h3&gt;

&lt;p&gt;NuGet 可以指定多个包源。既可以在 Visual Studio 中配置，也可以在配置文件中配置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-04-20-44-01.png&quot; alt=&quot;在 Visual Studio 中配置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-04-20-40-19.png&quot; alt=&quot;NuGet 配置文件&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nuget.org&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://api.nuget.org/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocolVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Debug&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C:\Users\lvyi\Desktop\TroukawDerjalyem\DejaiJacir\bin&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageSources&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;disabledPackageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft Visual Studio Offline Packages&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/disabledPackageSources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，只要有任何一个源不可用，那么你任何一个项目都别想再成功还原（restore）包了。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;某个国外的源因为某些不可描述的原因无法连通&lt;/li&gt;
  &lt;li&gt;某个源暂时挂掉了，服务不可用&lt;/li&gt;
  &lt;li&gt;某个本地的源，文件夹不存在了&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;是的，不管还有多少个或者，只要死了一个，还原都没有用了。&lt;/p&gt;

&lt;p&gt;这种情况，唯一的办法就是把那个不再可用的源从配置中删除，或者临时禁用掉出问题的源。&lt;/p&gt;

&lt;h3 id=&quot;不存在的版本新版本已修复&quot;&gt;不存在的版本（新版本已修复）&lt;/h3&gt;

&lt;p&gt;如果某个包的特定版本在所有源中不存在，那么安装此包的项目再也无法更新或者卸载此包了（也就别想再编译通过了）。&lt;/p&gt;

&lt;p&gt;不过目前这种问题只存在于旧的 &lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 形式的 NuGet 包管理系统中。如果已经升级成 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;，那么就没有这个问题了。&lt;/p&gt;

&lt;h3 id=&quot;编译不通过后无法安装和更新-nuget-包&quot;&gt;编译不通过后无法安装和更新 NuGet 包&lt;/h3&gt;

&lt;p&gt;有些情况下，会因为项目没有办法完成编译导致无法安装和更新某些 NuGet 包；但编译不通过其实就是这个 NuGet 包导致的（比如某个测试包）。大面积注释确保编译通过虽然说是一种可以尝试的手段，但毕竟还是太低效了。&lt;/p&gt;

&lt;p&gt;这时，通过手工修改项目文件来实现手工更新 NuGet 包不失为一种尝试手段。&lt;/p&gt;

&lt;h3 id=&quot;项目文件-sdk-的来回切换&quot;&gt;项目文件 Sdk 的来回切换&lt;/h3&gt;

&lt;p&gt;MSBuild 15.0 为项目文件的根节点 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 带来了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 属性，也就是说 Visual Studio 2017 开始支持。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt; 一文讲述了如何为项目文件添加 Sdk 属性，以便项目能够体验到最新的 Microsoft.NET.Sdk 编译体验。其中的 NuGet 原生支持是非常清爽的。&lt;/p&gt;

&lt;p&gt;升级时很清爽，降级就不爽了！这种情况会发生在新分支中进行了项目文件升级，随后切换回之前的分支；这时相当于在降级。但是，降级时会编译不通过，并提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Your project.json doesn’t have a runtimes section. You should add ‘“runtimes”: { “win”: { } }’ to your project.json and then re-run NuGet restore.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其实这是只有新的项目文件才会出现的编译错误，而错误原因是 NuGet 的缓存文件中与包引用相关的信息已经不正确了，需要运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; 重新更新此文件才行。但是，只有使用了 Sdk 风格的 csproj 文件才会在执行了此命令后重新生成正确的包引用缓存文件；原来的格式并不会生成此文件，也就是说，无法修复。&lt;/p&gt;

&lt;p&gt;唯一的解决办法就是清除项目中的所有 NuGet 缓存，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git clean -xdf&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;依赖的项目会自动转为依赖的-nuget-包&quot;&gt;依赖的项目会自动转为依赖的 NuGet 包&lt;/h3&gt;

&lt;p&gt;如果你给一个项目 A 打 NuGet 包，但这个项目引用此解决方案中的另一个项目 B。那么这时打包，NuGet 会认为 A 包依赖于 B 包。&lt;/p&gt;

&lt;p&gt;事实上，B 包极有可能是不存在的，也就是说，你打的 A 包并没有办法给大家正常使用。&lt;/p&gt;

&lt;h3 id=&quot;nugetgprops-和-nugetgtargets&quot;&gt;.nuget.g.props 和 .nuget.g.targets&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 作为 Sdk 的项目文件会自动在 obj 文件夹下生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;project.assets.json&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;$(ProjectName).csproj.nuget.cache&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;$(ProjectName).csproj.nuget.g.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(ProjectName).csproj.nuget.g.targets&lt;/code&gt; 文件；其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;.nuget.g.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.nuget.g.targets&lt;/code&gt; 中生成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 包中编译相关文件的代码。例如：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;14.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(ExcludeRestorePackageImports)' != 'true' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuGetPackageRoot)walterlv.demo.tools\3.0.27-alpha\build\Walterlv.Demo.Tools.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(NuGetPackageRoot)walterlv.demo.tools\3.0.27-alpha\build\Walterlv.Demo.Tools.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，有时会出现包中的文件并没有 Import 成功的情况，或者已经 Import，但却不明原因的无法完成编译。（我的 Visual Studio 版本 2017.7.4，Microsoft.NET.Sdk 版本 2.1.300。）&lt;/p&gt;

&lt;p&gt;这时，把这两个文件重新在 csproj 中 Import 一次却能正常。具体来说是这样（&lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo&lt;/code&gt; 是项目名称）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; Exists('obj\Walterlv.Demo.csproj.nuget.g.props') &quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj\Walterlv.Demo.csproj.nuget.g.props&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net45&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LanguageTargets&amp;gt;&lt;/span&gt;$(MSBuildToolsPath)\Microsoft.CSharp.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LanguageTargets&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cvte.Core&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.1.0.293&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; Exists('obj\Walterlv.Demo.csproj.nuget.g.targets') &quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj\Walterlv.Demo.csproj.nuget.g.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我们不通过直接修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;obj\Walterlv.Demo.csproj.nuget.g.props&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;obj\Walterlv.Demo.csproj.nuget.g.targets&lt;/code&gt; 文件是因为这两个文件不在版本管理中；而且如果执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; 后会重新生成。&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:38:58 +0000</pubDate>
        <link>https://blog.walterlv.com/post/problems-of-msbuild-and-nuget.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/problems-of-msbuild-and-nuget.html</guid>
        
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>阻止某个 NuGet 包意外升级</title>
        <description>&lt;p&gt;出于兼容性考虑，我们可能不再更新某个项目的 NuGet 包。典型的情况是软件版本进行了大规模的不兼容的升级，需要对旧格式的数据进行读取，以便迁移到新格式的数据。&lt;/p&gt;

&lt;p&gt;然而，团队开发的软件可能因为某个小伙伴不知道这样的历史问题，从而手抖将某个不应该更新的 NuGet 包更新了，于是迁移就挂了。&lt;/p&gt;

&lt;p&gt;本文提供了一种方法来避免某些特定 NuGet 包的升级。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你只关心结果，请直接前往&lt;a href=&quot;#%E6%9C%80%E7%BB%88%E8%A7%A3%E5%86%B3&quot;&gt;最后一节：终极解决方案&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;本文提供的方法仅适用于使用了 Sdk 风格的 csproj 项目文件。（当然并不是说旧的 csproj 不能使用这种方法，只是写法上会有差别，我没有去研究如何编写。）&lt;/p&gt;

&lt;p&gt;如果你的项目还在使用旧的 csproj 格式，推荐阅读 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt; 迁移成新格式之后再开始。&lt;/p&gt;

&lt;p&gt;作为例子，假设我们的项目文件是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Newtonsoft.Json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;11.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-29-17-02-09.png&quot; alt=&quot;不一致的 LiteDB 版本&quot; /&gt;&lt;/p&gt;

&lt;p&gt;LiteDB 是一个不应该被升级的 NuGet 包，但是最新版本已经是 4.1.4 了，很容易被团队中的其他小伙伴误升级。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-29-17-04-14.png&quot; alt=&quot;包管理器&quot; /&gt;&lt;br /&gt;
▲ 当小伙伴打开包管理器的时候，会发现包版本不一致，然后就不小心升级了&lt;/p&gt;

&lt;h2 id=&quot;思路&quot;&gt;思路&lt;/h2&gt;

&lt;p&gt;NuGet 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 来管理所有的包引用，于是我试图通过隐藏 LiteDB 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 节点来达到目的。&lt;/p&gt;

&lt;p&gt;而一个典型的隐藏方法便是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;。不在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 里面的属性和项是提前计算好的，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 里面的属性和项是编译时才计算的。&lt;/p&gt;

&lt;p&gt;可以通过阅读 &lt;a href=&quot;/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target&lt;/a&gt; 了解更多 Target 的知识。&lt;/p&gt;

&lt;p&gt;所以，我写了这样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，然后去掉前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其实这种改法并没有作用，可谁知道呢！ --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在这里把之前的 LiteDB 去掉了。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Newtonsoft.Json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;11.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这是新写的 Target，用来在编译期间引用 LiteDB。不过我不知道应该在什么时机执行。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceStaticLegacyPackage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;???&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还留了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 没有填，因为并不知道应该填什么。于是我打开了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 的文件夹 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\dotnet\sdk\2.1.300\Sdks&lt;/code&gt;，试图寻找时机。&lt;/p&gt;

&lt;p&gt;搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(PackageReference)&lt;/code&gt; 发现有很多的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 都依赖于一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CollectPackageReferences&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectResolvedSDKReferencesDesignTime&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Returns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ResolvedSDKReference)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;DependsOnTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResolveSDKReferencesDesignTime;CollectPackageReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从名称上可以猜测这是用来收集 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;于是我可以将我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CollectPackageReferences&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;不过我发现在这种情况下，NuGet 包管理器的界面中能够发现这个项目使用了旧版本。并且在安装了新版本的包后，将因为多次引用不同版本而导致编译不通过。&lt;/p&gt;

&lt;p&gt;所以，方案否决。&lt;/p&gt;

&lt;h2 id=&quot;最终解决&quot;&gt;最终解决&lt;/h2&gt;

&lt;p&gt;既然无法阻止发现这个 NuGet 包，那思路就换成无论如何更新，都无效好了。&lt;/p&gt;

&lt;p&gt;于是，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 和重新 &lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt; 固定版本来解决。&lt;/p&gt;

&lt;p&gt;下面是项目的最终解决源码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 其实这种改法并没有作用，可谁知道呢！ --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 无论这里版本填写多少，都不会有效。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.1.4&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Newtonsoft.Json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;11.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 通过移除正常的引用并替换成固定版本的引用，达到无论如何更新都无法生效的目的。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceStaticLegacyPackage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectPackageReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LiteDB&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这种 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的帮助下，无论如何更新 LiteDB 的 NuGet 版本，都能更新成功，但无法生效。&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:38:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/prevent-nuget-package-upgrade.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/prevent-nuget-package-upgrade.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>Sdk 风格的 csproj 对 WPF/UWP 支持不太好？有第三方 SDK 可以用！MSBuild.Sdk.Extras</title>
        <description>&lt;p&gt;自从微软推出 .NET Core 以来，新的项目文件格式以其优秀的可扩展性正吸引着更多项目采用。然而——微软官方的 WPF/UWP 项目模板依然还在采用旧的 csproj 格式！&lt;/p&gt;

&lt;p&gt;这只是因为——在 .NET Core 3.0 以前，基于 Microsoft.NET.Sdk 的官方 SDK 依然对 WPF/UWP 支持不够友好。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;为什么要使用第三方的-sdk&quot;&gt;为什么要使用第三方的 SDK？&lt;/h2&gt;

&lt;p&gt;关于项目文件格式的迁移，我和 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 都写过文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html&quot;&gt;从以前的项目格式迁移到 VS2017 新项目格式 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不过，这两篇文章中的迁移方法都是手动或半自动迁移的。而且迁移完毕之后，对新增的 WPF/UWP XAML 文件的支持非常不友好——&lt;strong&gt;新增的 XAML 文件是看不见的，除非手工去 csproj 文件中去掉自动生成的 Remove XAML 的代码。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这确实阻碍着我们在 WPF/UWP 项目中体会到新风格 csproj 的好处。&lt;/p&gt;

&lt;p&gt;微软在 Build 2018 大会上宣布，WPF/UWP 将能够在 .NET Core 3 中运行。想必，微软会为未来版本的 Microsoft.NET.Sdk 这样的官方 SDK 添加更多的 WPF/UWP 这类格式的支持吧！即便没有这样的原生支持，想必也会提供官方的扩展方案。&lt;/p&gt;

&lt;p&gt;但在此之前呢？感谢小伙伴 &lt;a href=&quot;https://github.com/KodamaSakuno&quot;&gt;KodamaSakuno (神樹桜乃)&lt;/a&gt; 提醒我第三方 SDK 的存在 —— MSBuild.Sdk.Extras。我想，在 .NET Core 3 推出之前，这是一种不错的中转方案。既能体会到新风格 csproj 格式的好处，也能在将来 .NET Core 3 官方支持后较快地迁移成官方版本。&lt;/p&gt;

&lt;h2 id=&quot;如何使用-msbuildsdkextras&quot;&gt;如何使用 MSBuild.Sdk.Extras&lt;/h2&gt;

&lt;p&gt;虽说是第三方 SDK，但实际使用的方便程度却如官方般简洁！只需要将 SDK 替换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild.Sdk.Extras/1.6.41&lt;/code&gt; 即可。1.6.41 是目前 MSBuild.Sdk.Extras 在 NuGet 上的最新版本，建议访问 &lt;a href=&quot;https://www.nuget.org/packages/MSBuild.Sdk.Extras/&quot;&gt;NuGet Gallery - MSBuild.Sdk.Extras&lt;/a&gt; 使用最新稳定版本。&lt;/p&gt;

&lt;p&gt;以下是最简同时支持 WPF 和 UWP 双框架的代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild.Sdk.Extras/1.6.41&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net471;uap10.0.17134&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 在刚刚指定完 uap10.0.17134 之后，等待 Visual Studio 还原需要等待好几分钟。&lt;/p&gt;

&lt;p&gt;另外，从 1.6.0 版本开始，为 WPF 和 Windows Forms 分别新增了一个属性，用于默认引用 WPF 或 Windows Forms 所需的程序集。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild.Sdk.Extras/1.6.41&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net471&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 以下是默认引用 WPF 相关依赖的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ExtrasEnableWpfProjectSetup&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ExtrasEnableWpfProjectSetup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 以下是默认引用 Windows Forms 相关依赖的属性 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;ExtrasEnableWinFormsProjectSetup &amp;gt;true&amp;lt;/ExtrasEnableWinFormsProjectSetup&amp;gt; --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从下图我们可以看出，设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExtrasEnableWpfProjectSetup&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 后，WPF 的类型将直接可用，而无需额外引用。（当然，不设置也是可以的，只是需要手动引用。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-26-07-15-26.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;没错，真的如此简单！在我们猜测的 .NET Core 3 支持 WPF/UWP 项目格式之前，这应该算是最简单的迁移方案了！&lt;/p&gt;

&lt;p&gt;至于项目结构的效果，可以看下图所示（包含 UWP 的多目标）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-26-07-18-49.png&quot; alt=&quot;net471 和 uap10.0.171341&quot; /&gt;&lt;/p&gt;

&lt;p&gt;相比于此前的手工迁移，使用此新格式创建出来的 XAML 文件是可见的，而且 .xaml.cs 也是折叠在 .xaml 之下，且能正常编译！（当然，咱们还得考虑 UWP 和 WPF 在 XAML 书写上的细微差异）&lt;/p&gt;

&lt;p&gt;官方提供了更多的使用方法，例如更简单的是安装 NuGet 包，而不修改 SDK。详见：&lt;a href=&quot;https://github.com/onovotny/MSBuildSdkExtras&quot;&gt;onovotny/MSBuildSdkExtras: Extra properties for MSBuild SDK projects&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/onovotny/MSBuildSdkExtras&quot;&gt;onovotny/MSBuildSdkExtras: Extra properties for MSBuild SDK projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:38:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-msbuild-sdk-extras-for-wpf-and-uwp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-msbuild-sdk-extras-for-wpf-and-uwp.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference) </title>
        <description>&lt;p&gt;最近我将项目格式进行了升级，从旧的 csproj 升级成了新的 csproj；NuGet 包管理的方式也从 &lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 升级成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;。然而迁移完才发现，这个项目竟然还依赖了大量的从 NuGet 2.x 时代发布的 NuGet 包，这些包并不能在 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 下好好工作。&lt;/p&gt;

&lt;p&gt;于是，我准备将所有这些包都进行升级。本文将介绍最简单的升级步骤。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;回顾遇到的问题&quot;&gt;回顾遇到的问题&lt;/h2&gt;

&lt;p&gt;如果你之前迁移过 csproj 文件，可能会遇到问题。关于迁移 csproj 文件，可以阅读：&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果你并没有迁移过 csproj 文件，只是升级了 NuGet 的包管理方式，也可能会遇到问题。关于自动迁移 NuGet 包管理方式，可以阅读：&lt;a href=&quot;/post/migrate-packages-config-to-package-reference&quot;&gt;自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;在自动迁移那篇文章中，我提到了一些兼容性问题，最大的莫过于 Install.ps1 脚本不再执行：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;使用 PackageReference 后，在安装和写在的过程中 install.ps1 脚本将不再执行。如果有一些行为依赖于此脚本，那么这个 NuGet 包的行为可能不正常。&lt;/p&gt;

  &lt;p&gt;但是，不用担心！install.ps1 的存在是因为 packages.config 不支持 PackageReference 中的一些新特性（例如 NuGet 包中新的目录结构，例如包中自带的 msbuild targets）。所以，如果 NuGet 包在发布时满足目录要求，那么即便 install.ps1 不用执行也能保证包的行为正常。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;虽然我提到不用担心，但其实旧的一些包里并没有准备 build 文件夹，也没有准备 props 或者 targets 文件。所以一小部分特别依赖于 install.ps1 的 NuGet 包是没有办法在新格式中生效的。&lt;/p&gt;

&lt;h2 id=&quot;最简升级步骤&quot;&gt;最简升级步骤&lt;/h2&gt;

&lt;p&gt;知道了问题所在，那么我们的根本便是将 Install.ps1 升级成新的 props 或者 targets。&lt;/p&gt;

&lt;p&gt;如果你不清楚 props 或者 targets 是什么意思，或者不知道怎么写它们，可以阅读我的另一篇文章&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;第一步将-installps1-翻译成-targets&quot;&gt;第一步：将 install.ps1 翻译成 targets&lt;/h3&gt;

&lt;p&gt;最简单的方法，直接去安装好 NuGet 的项目的 csproj 文件中去看究竟生成了那些代码。一般来说，这些 install.ps1 中多是生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点。&lt;/p&gt;

&lt;p&gt;而我们要做的，就是新建一个 build 文件夹，在其中新建 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageId&lt;/code&gt;.targets 文件，以便将生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点中的内容复制过去。前面那一句的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageId&lt;/code&gt; 指的是这个 NuGet 包的包 Id。比如，在我的例子中，是 Walterlv.NuGetDemo.targets。&lt;/p&gt;

&lt;p&gt;比如，生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 项目 csproj 文件 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvNuGetDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BinCopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;x64CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin_x64\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;x86CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin_x86\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(BinCopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x86CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x86&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x64CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，直接将这些文件复制到 PackageId.targets 文件中：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetDemo.targets 文件 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvNuGetDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;BinCopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;x64CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin_x64\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;x86CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\packages\Walterlv.NuGetDemo.1.2.3.0\tools\bin_x86\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(BinCopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x86CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x86&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x64CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，修改其中的路径，将相对于安装项目路径的地方更换成相对于此 targets 文件的路径：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Walterlv.NuGetDemo.targets 文件 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvNuGetDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;BinCopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\tools\bin\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;x64CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\tools\bin_x64\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;x86CopyItems&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\tools\bin_x86\*.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(BinCopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x86CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x86&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(x64CopyItems)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)x64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SkipUnchangedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第二步修改-nuspec-文件加入-targets&quot;&gt;第二步：修改 nuspec 文件，加入 targets&lt;/h3&gt;

&lt;p&gt;接着，去 nuspec 文件中，删除 Install.ps1 和 Uninstall.ps1，然后新增我们刚刚写的 targets 文件。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;files&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略其他一些文件 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;file&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tools\bin\DemoNativeLib.dll&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tools\bin&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 删除 &amp;lt;file src=&quot;tools\Install.ps1&quot; target=&quot;tools&quot;/&amp;gt; --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 删除 &amp;lt;file src=&quot;tools\Uninstall.ps1&quot; target=&quot;tools&quot;/&amp;gt; --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 省略其他一些文件 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;file&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\Walterlv.NuGetDemo.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/files&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;重新打包和测试-nuget-包&quot;&gt;重新打包和测试 NuGet 包&lt;/h3&gt;

&lt;p&gt;以上改完了之后，基本上就迁移完了。&lt;/p&gt;

&lt;p&gt;这样的改动是最小的，既能够保证旧的 packages.config 能够顺利迁移，也能保证新的 PackageReference 行为保持不变。&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:37:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/migrate-nuget-package-from-powershell-to-props-and-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/migrate-nuget-package-from-powershell-to-props-and-targets.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>理解 C# 项目 csproj 文件格式的本质和编译流程</title>
        <description>&lt;p&gt;写了这么多个 C# 项目，是否对项目文件 csproj 有一些了解呢？Visual Studio 是怎么让 csproj 中的内容正确显示出来的呢？更深入的，我能够自己扩展 csproj 的功能吗？&lt;/p&gt;

&lt;p&gt;本文将直接从 csproj 文件格式的本质来看以上这些问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;阅读本文，你将：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;可以通读 csproj 文件，并说出其中每一行的含义&lt;/li&gt;
  &lt;li&gt;可以手工修改 csproj 文件，以实现你希望达到的高级功能（更高级的，可以开始写个工具自动完成这样的工作了）&lt;/li&gt;
  &lt;li&gt;理解新旧 csproj 文件的差异，不至于写工具解析和修改 csproj 文件的时候出现不兼容的错误&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;csproj-里面是什么&quot;&gt;csproj 里面是什么？&lt;/h2&gt;

&lt;h3 id=&quot;总览-csproj-文件&quot;&gt;总览 csproj 文件&lt;/h3&gt;

&lt;p&gt;相信你一定见过传统的 csproj 文件格式。就算你几乎从来没主动去看过里面的内容，在版本管理工具中解冲突时也在里面修改过内容。&lt;/p&gt;

&lt;p&gt;不管你是新手还是老手，一定都会觉得这么长这么复杂的文件一定不是给人类阅读的。你说的是对的！传统 csproj 文件中有大量的重复或者相似内容，只为 msbuild 和 Visual Studio 能够识别整个项目的属性和结构，以便正确编译项目。&lt;/p&gt;

&lt;p&gt;不过，既然这篇文章的目标是理解 csproj 文件格式的本质，那我当然不会把这么复杂的文件内容直接给你去阅读。&lt;/p&gt;

&lt;p&gt;我已经将整个文件结构进行了极度简化，然后用思维导图进行了分割。总结成了下图，如果先不关注文件的细节，是不是更容易看懂了呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-12-31-53.png&quot; alt=&quot;传统的 csproj 文件格式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你此前也阅读过我的其他博客，会发现我一直在试图推荐使用新的 csproj 格式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/configure-projects-to-target-multiple-platforms&quot;&gt;让一个 csproj 项目指定多个开发框架&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么新格式和旧格式究竟有哪些不同使得新的格式如此简洁？&lt;/p&gt;

&lt;p&gt;于是，我将新的 csproj 文件结构也进行简化，用思维导图进行了分割。总结成了下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-07-08-41-22.png&quot; alt=&quot;跨平台的 csproj 文件格式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比较两个思维导图之后，是不是发现其实两者本是相同的格式。如果忽略我在文字颜色上做的标记，其实两者的差异几乎只在文件开头是否有一个 xml 文件标记（&lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;&lt;/code&gt;）。我在文字颜色上的标记代表着这部分的部件是否是可选的，白色代表必须，灰色代表可选；而更接近背景色的灰色代表一般情况下都是不需要的。&lt;/p&gt;

&lt;p&gt;我把两个思维导图放到一起方便比较：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-07-08-42-05.png&quot; alt=&quot;新旧两种 csproj 文件格式的差异&quot; /&gt;&lt;/p&gt;

&lt;p&gt;会发现，传统格式中 &lt;code class=&quot;highlighter-rouge&quot;&gt;xml 声明&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Project 节点&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Import (props)&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Import (targets)&lt;/code&gt; 都是必要的，而新格式中只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project 节点&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 是必要的。&lt;/p&gt;

&lt;p&gt;是什么导致了这样的差异？在了解 csproj 文件中各个部件的作用之前，这似乎很难回答。&lt;/p&gt;

&lt;h3 id=&quot;了解-csproj-中的各个部件的作用&quot;&gt;了解 csproj 中的各个部件的作用&lt;/h3&gt;

&lt;p&gt;xml 声明部分完全没有在此解释的必要了，为兼容性提供了方便，详见：&lt;a href=&quot;https://en.wikipedia.org/wiki/XML#International_use&quot;&gt;XML - Wikipedia&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;接下来，我们不会依照部件出现的顺序安排描述的顺序，而是按照关注程度排序。&lt;/p&gt;

&lt;h3 id=&quot;propertygroup&quot;&gt;PropertyGroup&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 是用来存放属性的地方，这与它的名字非常契合。那么里面放什么属性呢？答案是——什么都能放！&lt;/p&gt;

&lt;p&gt;在这里写属性就像在代码中定义属性或变量一样，只要写了，就会生成一个指定名称的属性。&lt;/p&gt;

&lt;p&gt;比如，我们写：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Foo&amp;gt;&lt;/span&gt;walterlv is a 逗比&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Foo&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;那么，就会生成一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 属性，值为字符串 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv is a 逗比&lt;/code&gt;。至于这个属性有什么用，那就不归这里管了。&lt;/p&gt;

&lt;p&gt;这些属性的含义完全是由外部来决定的，例如在旧的 csproj 格式中，编译过程中会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworkVersion&lt;/code&gt; 属性，以确定编译应该使用的 .NET Framework 目标框架的版本（是 v4.5 还是 v4.7）。在新的 csproj 格式中，编译过程会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt; 属性来决定编译应该使用的目标框架（是 net47 还是 netstandard2.0）。具体是编译过程中的哪个环节哪个组件使用了此属性，我们后面会说。&lt;/p&gt;

&lt;p&gt;从这个角度来说，如果你没有任何地方用到了你定义的属性，那为什么还要定义它呢？是的——这只是浪费。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 可以定义很多个，里面都可以同等地放属性。至于为什么会定义多个，原因无外乎两个：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;为了可读性——将一组相关的属性放在一起，便于阅读和理解意图（旧的 csproj 谈不上什么可读性）&lt;/li&gt;
  &lt;li&gt;为了加条件——有的属性在 Debug 和 Release 下不一样（例如条件编译符 &lt;code class=&quot;highlighter-rouge&quot;&gt;DefineConstants&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;额外说一下，&lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt; 这两个值其实是在某处一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; 的属性定义的，它们其实只是普通的字符串而已，没什么特殊的意义，只是有很多的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 加上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt; 的判断条件才使得不同的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; 具有不同的其他属性，最终表现为编译后的巨大差异。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; 属性可以放任意字符串，所以甚至可以定义一个非 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debug&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt; 的配置（例如用于性能专项测试）也是可以的。&lt;/p&gt;

&lt;h3 id=&quot;itemgroup&quot;&gt;ItemGroup&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 是用来指定集合的地方，这与它的名字非常契合。那么这集合里面放什么项呢？答案是——什么都能放！&lt;/p&gt;

&lt;p&gt;是不是觉得这句话跟前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 句式一模一样？是的——就是一模一样！csproj 中的两个大头都这样不带语义，几乎可以说明 csproj 文件是不包含语义的，它能够用来做什么事情纯属由其他模块来指定；这为 csproj 文件强大的扩展性提供了格式基础。&lt;/p&gt;

&lt;p&gt;既然什么都能放，那我们放这些吧：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 天才&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;天才向左，逗比向右&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;逗比属性额外加成&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是我们就有 4 个类型为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的项了，至于这 4 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 项有什么作用，那就不归这里管了。&lt;/p&gt;

&lt;p&gt;这些项的含义与 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 一样也是由外部来决定。具体是哪个外部，我们稍后会说。但是我们依然有一些常见的项可以先介绍介绍：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Reference&lt;/code&gt; 引用某个程序集&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 引用某个 NuGet 包&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectReference&lt;/code&gt; 引用某个项目&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 常规的 C# 编译&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 没啥特别的编译选项，就为了执行一些通用的操作（或者是只是为了在 Visual Studio 列表中能够有一个显示）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Folder&lt;/code&gt; 一个空的文件夹，也没啥用（不过标了这个文件夹，Visual Studio 中就能有一个文件夹的显式，即便实际上这个文件夹可能不存在）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 也可以放很多组，一样是为了提升可读性或者增加条件。&lt;/p&gt;

&lt;h3 id=&quot;import&quot;&gt;Import&lt;/h3&gt;

&lt;p&gt;你应该注意到在前面的思维导图中，无论是 Sdk 风格的 csproj 还是旧 csproj 文件，我都写了两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 节点。其实它们本质上是完全一样的，只不过在含义上有不同。前面我们了解到 csproj 文件致力于脱离语义，所以分开两个地方写几乎只是为了可读性考虑。&lt;/p&gt;

&lt;p&gt;那么前面那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 和后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 在含义上有何区别？思维导图的括号中我已说明了含义。前面是为了导入属性（props），后面是为了导入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Targets&lt;/code&gt;。属性就是前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 中说的那些属性和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 里说的那些项；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Targets&lt;/code&gt; 是新东西，这才是真正用来定义编译流程的关键，由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Targets&lt;/code&gt; 是所有节点里面最复杂的部分，所以我们放到最后再说。&lt;/p&gt;

&lt;p&gt;那么，被我们 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 进来的那些文件是什么呢？用两种扩展名，定义属性的那一种是 &lt;code class=&quot;highlighter-rouge&quot;&gt;.props&lt;/code&gt;，定义行为的那一种是 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这两种文件除了含义不同以外，内容的格式都是完全一样的——而且——就是 csproj 文件的那种格式！没错，也包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Targets&lt;/code&gt;。只不过，相比于对完整性有要求的 csproj 文件来说，这里可以省略更多的节点。由于有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 的存在，所以一层一层地嵌套 &lt;code class=&quot;highlighter-rouge&quot;&gt;props&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;targets&lt;/code&gt; 都是可能的。&lt;/p&gt;

&lt;p&gt;说了这么多，让我们来看其中两个 .props 文件吧。&lt;/p&gt;

&lt;p&gt;先看看旧格式 csproj 文件中第一行一定会 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 的那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Common.props&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 文件太长，做了大量删减 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportByWildcardBeforeMicrosoftCommonProps&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportByWildcardBeforeMicrosoftCommonProps&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportByWildcardAfterMicrosoftCommonProps&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(ImportByWildcardAfterMicrosoftCommonProps)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportByWildcardAfterMicrosoftCommonProps&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportUserLocationsByWildcardBeforeMicrosoftCommonProps&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(ImportUserLocationsByWildcardBeforeMicrosoftCommonProps)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportUserLocationsByWildcardBeforeMicrosoftCommonProps&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportUserLocationsByWildcardAfterMicrosoftCommonProps&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(ImportUserLocationsByWildcardAfterMicrosoftCommonProps)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportUserLocationsByWildcardAfterMicrosoftCommonProps&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImportDirectoryBuildProps&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(ImportDirectoryBuildProps)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImportDirectoryBuildProps&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 文件太长，做了大量删减 --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;文件太长，做了大量删减，但也可以看到文件格式与 csproj 几乎是一样的。此文件中，根据其他属性的值有条件地定义了另一些属性。&lt;/p&gt;

&lt;p&gt;再看看另一个 MSTest 单元测试项目中被隐式 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 进 csproj 文件中的 .props 文件。（&lt;em&gt;所谓隐式地 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt;，只不过是被间接地引入，在 csproj 文件中看不到这个文件名而已。至于如何间接引入，因为涉及到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Targets&lt;/code&gt;，所以后面一起说明。&lt;/em&gt;）&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Link&amp;gt;&lt;/span&gt;Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Link&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;CopyToOutputDirectory&amp;gt;&lt;/span&gt;PreserveNewest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CopyToOutputDirectory&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Visible&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Visible&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Content&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Link&amp;gt;&lt;/span&gt;Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Link&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;CopyToOutputDirectory&amp;gt;&lt;/span&gt;PreserveNewest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CopyToOutputDirectory&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Visible&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Visible&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Content&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Link&amp;gt;&lt;/span&gt;Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Link&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;CopyToOutputDirectory&amp;gt;&lt;/span&gt;PreserveNewest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CopyToOutputDirectory&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Visible&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Visible&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Content&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;此文件中将三个 dll 文件从 MSTest 的 NuGet 包中以链接的形式包含到项目中，并且此文件在 Visual Studio 的解决方案列表中不可见。&lt;/p&gt;

&lt;p&gt;可以看出，引入的 props 文件可以实现几乎与 csproj 文件中一样的功能。&lt;/p&gt;

&lt;p&gt;那么，既然 csproj 文件中可以完全实现这样的功能，为何还要单独用 &lt;code class=&quot;highlighter-rouge&quot;&gt;props&lt;/code&gt; 文件来存放呢？原因显而易见了——为了在多个项目中使用，&lt;strong&gt;一处更新，到处生效&lt;/strong&gt;。所以有没有觉得很好玩——如果把版本号单独放到 props 文件中，就能做到一处更新版本号，到处更新版本号啦！&lt;/p&gt;

&lt;h3 id=&quot;target&quot;&gt;Target&lt;/h3&gt;

&lt;p&gt;终于开始说 Target 了。为什么会这么期待呢？因为前面埋下的各种伏笔几乎都要在这一节点得到解释了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-08-08-06-56.png&quot; alt=&quot;我啥时候说有伏笔了？&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一般来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点写在 csproj 文件的末尾，但这个并不是强制的。Targets 是一种非常强大的功能扩展方式，支持 msbuild 预定义的一些指令，支持命令行，甚至支持使用 C# 直接编写（当然编译成 dll 会更方便些），还支持这些的排列组合和顺序安排。而我们实质上的编译过程便全部由这些 Targets 来完成。我们甚至可以直接说——&lt;strong&gt;编译过程就是靠这些 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的组合来完成的&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;如果你希望全面了解 Targets，推荐直接阅读微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets?wt.mc_id=MVP&quot;&gt;MSBuild Targets&lt;/a&gt;，而本文只会对其进行一些简单的概述。当然如果你非常感兴趣，还可以阅读我另外几篇关于 Target 使用相关的文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/migrate-nuget-package-from-powershell-to-props-and-targets&quot;&gt;如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference) - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不过，为了简单地理解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，我依然需要借用官方文档的例子作为开头。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Construct&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Csc&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sources=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Compile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;这份代码定义了一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Construct&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，这是随意取的一个名字，并不重要——但是编译过程中会执行这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;。在这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 内部，使用了一个 msbuild 自带的名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Csc&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。这里我们再次引入了一个新的概念 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 内部真正完成逻辑性任务的核心；或者说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 其实只是一种容器，本身并不包含编译逻辑，但它的内部可以存放 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 来实现编译逻辑。一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 内可以放多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;，不止如此，还能放 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt;，不过这是仅在编译期生效的属性和项了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(Compile)&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 中所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 类型节点的集合。还记得我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 小节时说到每一种 &lt;code class=&quot;highlighter-rouge&quot;&gt;Item&lt;/code&gt; 的含义由外部定义吗？是的，就是在这里定义的！本身并没有什么含义，但它们作为参数传入到了具体的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 之后便有了此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 指定的含义。&lt;/p&gt;

&lt;p&gt;于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target Name=&quot;Construct&quot;&amp;gt;&amp;lt;Csc Sources=&quot;@(Compile)&quot; /&amp;gt;&amp;lt;/Target&amp;gt;&lt;/code&gt; 的含义便是调用 msbuild 内置的 C# 编译器编译所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 类型的项。&lt;/p&gt;

&lt;p&gt;如果后面定义了一个跟此名称一样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，那么后一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 就会覆盖前一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，导致前一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 失效。&lt;/p&gt;

&lt;p&gt;再次回到传统的 csproj 文件上来，每一个传统格式的 csproj 都有这样一行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildToolsPath)\Microsoft.CSharp.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而引入的这份 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件便包含了 msbuild 定义的各种核心编译任务。只要引入了这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件，便能使用 msbuild 自带的编译任务完成绝大多数项目的编译。你可以自己去查看此文件中的内容，相信有以上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的简单介绍，应该能大致理解其完成编译的流程。这是我的地址：&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\Microsoft.CSharp.targets&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;project&quot;&gt;Project&lt;/h3&gt;

&lt;p&gt;所有的 csproj 文件都是以 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 节点为根节点。既然是根节点为何我会在最后才说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 呢？因为这可是一个大悬念啊！本文一开始就描述了新旧两款 csproj 文件格式的差异，你也能从我的多篇博客中感受到新格式带来的各种好处；而简洁便是新格式中最大的好处之一。它是怎么做到简洁的呢？&lt;/p&gt;

&lt;p&gt;就靠 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 节点了。&lt;/p&gt;

&lt;p&gt;注意到新格式中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 节点有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt; 属性吗？因为有此属性的存在，csproj 文件才能如此简洁。因为——所谓 Sdk，其实是一大波 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt; 文件的集合。它帮我们导入了公共的属性、公共的编译任务，还帮我们自动将项目文件夹下所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;**\*.cs&lt;/code&gt; 文件都作为 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 的项引入进来。&lt;/p&gt;

&lt;p&gt;如果你希望看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 都引入了哪些文件，可以去本机安装的 msbuild 或 dotnet 的目录下查看。当我使用 msbuild 编译时，我的地址：&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\dotnet\sdk\2.1.300\Sdks\Microsoft.NET.Sdk\build\&lt;/code&gt;。比如你可以从此文件夹里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.GenerateAssemblyInfo.targets&lt;/code&gt; 文件中发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyInfo.cs&lt;/code&gt; 文件是如何自动生成及生效的。&lt;/p&gt;

&lt;h2 id=&quot;编译器是如何将这些零散的部件组织起来的&quot;&gt;编译器是如何将这些零散的部件组织起来的？&lt;/h2&gt;

&lt;p&gt;这里说的编译器几乎只指 msbuild 和 Roslyn，前者基于 .NET Framework，后者基于 .NET Core。不过，它们在处理我们的项目文件时的行为大多是一致的——至少对于通常项目来说如此。&lt;/p&gt;

&lt;p&gt;我们前一部分介绍每个部件的时候，已经简单说了其组织方式，这里我们进行一个回顾和总结。&lt;/p&gt;

&lt;p&gt;当 Visual Studio 打开项目时，它会解析里面所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 节点，确认应该引入的 .props 和 .targets 文件都引入了。随后根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 里面设置的属性正确显示属性面板中的状态，根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 中的项正确显示解决方案管理器中的引用列表、文件列表。——这只是 Visual Studio 做的事情。&lt;/p&gt;

&lt;p&gt;在编译时，msbuild 或 Roslyn 还会重新做一遍上面的事情——毕竟这两个才是真正的编译器，可不是 Visual Studio 的一部分啊。随后，执行编译过程。它们会按照 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 指定的先后顺序来安排不同 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的执行，当执行完所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，便完成了编译过程。&lt;/p&gt;

&lt;h2 id=&quot;新旧-csproj-在编译过程上有什么差异&quot;&gt;新旧 csproj 在编译过程上有什么差异？&lt;/h2&gt;

&lt;p&gt;相信读完前面两个部分之后，你应该已经了解到在格式本身上，新旧格式之间其实并没有什么差异。或者更严格来说，差异只有一条——新格式在 Project 上指定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk&lt;/code&gt;。真正造成新旧格式在行为上的差别来源于默认为我们项目 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 进来的那些 .props 和 .targets 不同。新格式通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 为我们导入了更现代化的 .props 和 .targets，而旧格式需要考虑到兼容性压力，只能引入旧的那些 .targets。&lt;/p&gt;

&lt;p&gt;新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 以不兼容的方式支持了各种新属性，例如新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt; 代替旧的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworkVersion&lt;/code&gt;，使得我们的 C# 项目可以脱离 .NET Framework，引入其他各种各样的目标框架，例如 netstandard2.0、net472、uap10.0 等（可以参考 &lt;a href=&quot;https://blog.lindexi.com/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html#%E5%A4%9A%E4%B8%AA%E6%A1%86%E6%9E%B6&quot;&gt;从以前的项目格式迁移到 VS2017 新项目格式 - 林德熙&lt;/a&gt;）了解可以使用那些目标框架。&lt;/p&gt;

&lt;p&gt;新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt; 以不兼容的方式原生支持了 NuGet 包管理。也就是说我们可以在不修改 csproj 的情况之下通过 NuGet 包来扩展 csproj 的功能。而旧的格式需要在 csproj 文件的末尾添加如下代码才可以获得其中一个 NuGet 包功能的支持：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\packages\Walterlv.Demo.3.0.0-beta.6\build\Walterlv.Demo.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('..\packages\Walterlv.Demo.3.0.0-beta.6\build\Walterlv.Demo.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EnsureNuGetPackageBuildImports&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PrepareForBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorText&amp;gt;&lt;/span&gt;This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorText&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!Exists('..\packages\Walterlv.Demo.3.0.0-beta.6\build\Walterlv.Demo.targets')&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$([System.String]::Format('$(ErrorText)', '..\packages\Walterlv.Demo.3.0.0-beta.6\build\Walterlv.Demo.targets'))&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过好在 NuGet 4.x 以上版本在安装 NuGet 包时自动为我们在 csproj 中插入了以上代码。&lt;/p&gt;

&lt;h2 id=&quot;更多资料&quot;&gt;更多资料&lt;/h2&gt;

&lt;p&gt;如果你在阅读本文时还有更多问题，可以阅读我和朋友的其他相关博客，也可以随时在下方向我留言。如果没有特别原因，我都是在一天之内进行回复。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量了） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/configure-projects-to-target-multiple-platforms&quot;&gt;让一个 csproj 项目指定多个开发框架 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.github.io/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html#%E5%A4%9A%E4%B8%AA%E6%A1%86%E6%9E%B6&quot;&gt;从以前的项目格式迁移到 VS2017 新项目格式 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/migrate-packages-config-to-package-reference&quot;&gt;自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:37:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/understand-the-csproj.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/understand-the-csproj.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference</title>
        <description>&lt;p&gt;在前段时间我写了一篇迁移 csproj 格式的博客 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;，不过全过程是手工进行的，而且到最后处理 XAML 问题也非常头疼。&lt;/p&gt;

&lt;p&gt;现在，我们可以利用工具自动地完成这个过程。当然，工具并不将 csproj 格式进行迁移，而是在不迁移格式的情况下，使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 方式 NuGet 引用带来的好处。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;自动升级&quot;&gt;自动升级&lt;/h2&gt;

&lt;p&gt;&lt;del&gt;下载安装 Visual Studio 插件 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=CloudNimble.NuGetPackageReferenceUpgrader&quot;&gt;NuGet PackageReference Upgrader&lt;/a&gt;。在安装完成之后，再次启动 Visual Studio，则可以开始迁移。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;更新：自 Visual Studio 2017 的 15.7 版本开始，迁移工具已经自带到 Visual Studio 中。详情请参见：&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/migrate-packages-config-to-package-reference&quot;&gt;Migrating from package.config to PackageReference formats - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;只有一个步骤&lt;/strong&gt;：在 &lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 文件上点击右键，选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Upgrade to PackageReference&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-03-16-40-10.png&quot; alt=&quot;Migrate packages.config to PackageReference&quot; /&gt;&lt;/p&gt;

&lt;!-- ![Upgrade to PackageReference](/static/posts/2018-04-24-16-03-17.png) --&gt;

&lt;p&gt;在弹出的界面中，选择包的版本，确定即可完成一个项目的迁移。&lt;/p&gt;

&lt;p&gt;相比于之前写的手工迁移，自动迁移方式没有改变 csproj 的格式，而只是将 NuGet 的引用方式改成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;。具体有哪些好处，可以阅读 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;检查升级后的兼容性问题&quot;&gt;检查升级后的兼容性问题&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 的 NuGet 包的管理方式有些功能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 没有的。当然，没有这些功能是因为“不需要”，而不是“还没支持”；所以大部分的迁移都不会发生问题（除非发布包使用的是特别老旧的 nuget.exe，或者发布者利用了一些丧心病狂的黑科技）。&lt;/p&gt;

&lt;p&gt;在 Visual Studio 2017 的 15.7 版本以上自带的迁移工具中，会自动列出可能的兼容性问题。&lt;/p&gt;

&lt;h3 id=&quot;installps1-脚本将失效&quot;&gt;install.ps1 脚本将失效&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 后，在安装和写在的过程中 &lt;code class=&quot;highlighter-rouge&quot;&gt;install.ps1&lt;/code&gt; 脚本将不再执行。如果有一些行为依赖于此脚本，那么这个 NuGet 包的行为可能不正常。&lt;/p&gt;

&lt;p&gt;但是，不用担心！&lt;code class=&quot;highlighter-rouge&quot;&gt;install.ps1&lt;/code&gt; 的存在是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 不支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 中的一些新特性（例如 NuGet 包中新的目录结构，例如包中自带的 msbuild targets）。所以，如果 NuGet 包在发布时满足目录要求，那么即便 &lt;code class=&quot;highlighter-rouge&quot;&gt;install.ps1&lt;/code&gt; 不用执行也能保证包的行为正常。&lt;/p&gt;

&lt;h3 id=&quot;使用-content-方式指定的内容资产将失效&quot;&gt;使用 content 方式指定的内容资产将失效&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;contentFiles&lt;/code&gt; 来管理内容资产，这样可以更好地在多个依赖包之间传递和共享。而此前 &lt;code class=&quot;highlighter-rouge&quot;&gt;content&lt;/code&gt; 指定的资产将失效。&lt;/p&gt;

&lt;p&gt;建议检查所有依赖的 NuGet 包，如果你有权限修改部分依赖包，那么请使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;contentFiles&lt;/code&gt; 来替代 &lt;code class=&quot;highlighter-rouge&quot;&gt;content&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;xdt-变换将失效&quot;&gt;XDT 变换将失效&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 后，在安装和写在的过程中 XDT 转换将不会执行，并且会忽略 .xdt 文件。&lt;/p&gt;

&lt;p&gt;在 Web 应用开发中会更留意这个问题。&lt;/p&gt;

&lt;h3 id=&quot;lib-根目录中的程序集将被忽略&quot;&gt;lib 根目录中的程序集将被忽略&lt;/h3&gt;

&lt;p&gt;lib 文件夹内的程序集都应该按照目标框架建立子文件夹，例如 net45、netstandard2.0、netcoreapp2.0。&lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 要求只能引用在某个目标框架下的程序集。&lt;/p&gt;

&lt;p&gt;如果是使用默认的方式创建的 NuGet 包，基本上不会遇到这样的问题。除非你在创建 NuGet 包时有自定义操作在根目录放了程序集。&lt;/p&gt;

&lt;h2 id=&quot;解决升级后的编译错误&quot;&gt;解决升级后的编译错误&lt;/h2&gt;

&lt;p&gt;最可能出现的编译问题是 NuGet 包引用的版本冲突。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;packages.config&lt;/code&gt; 方式的包引用要求在 csproj 文件中显式指定一个依赖的包的版本，于是无论依赖使用了哪个版本，最终都由显式指定的版本来指定。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 的引用方式是自动管理依赖版本的，只要每个包都在允许的版本范围之内，就自动选择版本，并显示在解决方案的引用中。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 出现依赖冲突的提示通常是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Version conflict detected for NuGet.Versioning. Reference the package directly from the project to resolve this issue.
NuGet.Packaging 3.5.0 -&amp;gt; NuGet.Versioning (= 3.5.0)
NuGet.Configuration 4.0.0 -&amp;gt; NuGet.Versioning (= 4.0.0)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，引用的两个不同的包要求依赖相同包的不同版本，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 无法隐式推断依赖包的版本。这时需要将项目的依赖方式改为之前的方式。&lt;/p&gt;

&lt;p&gt;当然，在制作和发布 NuGet 包时，尽量使用非特定版本的依赖包，能够极大地避免这种问题带来的影响。关于如何指定非特定版本的依赖包，可以阅读 &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards?wt.mc_id=MVP&quot;&gt;Version ranges and wildcards 版本范围和通配符&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/migrate-packages-config-to-package-reference?wt.mc_id=MVP&quot;&gt;Migrating from package.config to PackageReference formats - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/NuGet/Home/wiki/packages.config-(PC)-to-PackageReference-(PR)-Migrator&quot;&gt;packages.config (PC) to PackageReference (PR) Migrator · NuGet/Home Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:37:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/migrate-packages-config-to-package-reference.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/migrate-packages-config-to-package-reference.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>语义版本号（Semantic Versioning）</title>
        <description>&lt;p&gt;版本号格式不陌生吧，.NET 传统的版本号格式类似这样 1.5.1254.0。本文将推荐一种新的版本号格式——语义版本号，格式类似这样 1.4.6-beta。我推荐语义版本号是因为这样的版本号自包含语义，而且这样的语义能够在版本库中体现出来。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;传统的版本号&quot;&gt;传统的版本号&lt;/h2&gt;

&lt;p&gt;如果你只是知道传统版本号由四个部分组成，那么建议去官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/app-domains/assembly-versioning?wt.mc_id=MVP&quot;&gt;Assembly Versioning&lt;/a&gt; 了解一下这种版本号的定义。它分为 &lt;code class=&quot;highlighter-rouge&quot;&gt;主版本号&lt;/code&gt;.&lt;code class=&quot;highlighter-rouge&quot;&gt;次版本号&lt;/code&gt;.&lt;code class=&quot;highlighter-rouge&quot;&gt;构建号&lt;/code&gt;.&lt;code class=&quot;highlighter-rouge&quot;&gt;修订号&lt;/code&gt; 四个部分，但是后面的一个或多个部分可以省略。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-12-19-48-26.png&quot; alt=&quot;AssemblyVersion&quot; /&gt;&lt;/p&gt;

&lt;p&gt;例如，1.5.1254.0 表示主版本号是 1，次版本号是 5；在 1.5 的版本下，第 1255 次构建，并且在这次构建之后没有进行修订。如果你是一个库的发布者，那么主版本号的改变意味着 API 出现不兼容的修改；次版本号改变意味着 API 出现兼容的修改（通常是新增）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-12-19-48-01.png&quot; alt=&quot;new Version()&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而我们如何能够准确地向所有人传递这样的版本规则呢？当我们在向全世界提供一个库（比如 NuGet 包）的时候，我们怎么让团队所有人都知道我们正在为哪个版本开发新功能呢？我们又应该在何时更新程序集或者 NuGet 的版本号呢（在功能开发开始？差不多完成？临近发布？）？&lt;/p&gt;

&lt;p&gt;传统的版本号记录不了这些信息，于是我们不得不用一些额外的方式来记录，这就增加了维护成本。&lt;/p&gt;

&lt;h2 id=&quot;语义版本号&quot;&gt;语义版本号&lt;/h2&gt;

&lt;p&gt;语义版本号由五个部分组成 &lt;code class=&quot;highlighter-rouge&quot;&gt;主版本号&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;次版本号&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;补丁号&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;预发布版本标签&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;构建号&lt;/code&gt;。举例看看语义版本号是什么样的吧（摘自 &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/package-versioning?wt.mc_id=MVP&quot;&gt;NuGet Package Version Reference&lt;/a&gt;）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.0.1&lt;/li&gt;
  &lt;li&gt;1.0.1-rc&lt;/li&gt;
  &lt;li&gt;1.0.1-beta&lt;/li&gt;
  &lt;li&gt;1.0.1-alpha2&lt;/li&gt;
  &lt;li&gt;1.0.1-alpha&lt;/li&gt;
  &lt;li&gt;1.0.1-aaa&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NuGet 4.3.0 以上，并且 Visual Studio 2017 的 15.3 以上版本开始支持语义版本号 2.0（Semantic Versioning 2.0.0）。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.0.0-alpha.1
    &lt;ul&gt;
      &lt;li&gt;2.0 版本的语义版本号在预发布标签后面使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; 来区分预发布的不同版本，这样就能避免 &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha2&lt;/code&gt; 在字符串比较上大于 &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha10&lt;/code&gt; 的问题。（否则得写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha02&lt;/code&gt; 了。）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;1.0.0+&lt;code class=&quot;highlighter-rouge&quot;&gt;githash&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;2.0 版本的语义版本号在最后使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; 来表示 git 版本库相关的信息，这样为持续集成（CI）时自动生成版本号提供了方便。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;1.0.0-beta.5+4
    &lt;ul&gt;
      &lt;li&gt;表示这是准备发布 1.0.0 的第 5/6 个 beta 版本之后，又新增了 4 个 git 提交。（是不是意义更加明确？）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如何在项目中使用语义版本号&quot;&gt;如何在项目中使用语义版本号？&lt;/h2&gt;

&lt;p&gt;如果你希望方便，在执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet pack&lt;/code&gt; 命令之后能够直接得到使用语义版本号的 NuGet 包，那么你必须拥有一个新格式的 csproj，就是 .NET Core 带来的那种新格式。如果你的格式是旧的，可以阅读我的另一篇文章 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt; 迁移成新格式。&lt;/p&gt;

&lt;p&gt;这样，在 csproj 文件中将版本号写为以下方式即可：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.6.2-beta&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;PackageId&amp;gt;Walterlv.DemoPackage&amp;lt;/PackageId&amp;gt; --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;TargetFrameworks&amp;gt;netstandard2.0;net471&amp;lt;/TargetFrameworks&amp;gt; --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你还可以考虑在编译的时候进行改变，即执行编译命令的时候传入版本号：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 以下三种都行&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/p:Version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6.2&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-beta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/p:Version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6.2&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-beta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/p:Version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6.2&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-beta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，你还可以使用响应文件来简化参数，详情可阅读我的另一篇博客 &lt;a href=&quot;/post/msbuild-response-files&quot;&gt;使用 MSBuild 响应文件 (rsp) 来指定 dotnet build 命令行编译时的大量参数&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果希望自动化地在项目中生成语义版本号，可阅读我的另一篇博客 &lt;a href=&quot;/post/automatically-semantic-versioning-using-git-version-task&quot;&gt;使用 GitVersion 在编译或持续构建时自动使用语义版本号（Semantic Versioning）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;如何用-cnet-支持语义版本号&quot;&gt;如何用 C#/.NET 支持语义版本号？&lt;/h2&gt;

&lt;p&gt;可参考林德熙的博客：&lt;a href=&quot;https://lindexi.github.io/lindexi/post/C-%E4%BD%BF%E7%94%A8%E8%BD%AC%E6%8D%A2%E8%AF%AD%E4%B9%89%E7%89%88%E6%9C%AC%E5%8F%B7.html&quot;&gt;C# 使用转换语义版本号&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0 - Semantic Versioning&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xavierdecoster.com/post/2013/04/29/semantic-versioning-auto-incremented-nuget-package-versions.html&quot;&gt;Semantic Versioning &amp;amp; auto-incremented NuGet package versions - Xavier Decoster&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/package-versioning?wt.mc_id=MVP&quot;&gt;NuGet Package Version Reference - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/prerelease-packages?wt.mc_id=MVP&quot;&gt;Pre-release versions in NuGet packages - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/03/versioning-nuget-packages-cd-1/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 1 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/26/versioning-nuget-packages-cd-3/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.nuget.org/20140924/supporting-semver-2.0.0.html&quot;&gt;Supporting Semantic Versioning 2.0.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:36:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/semantic-version.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/semantic-version.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦）</title>
        <description>&lt;p&gt;知道了 csproj 文件中的一些常用属性，修改文件的时候就不会写很多的垃圾代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;“项目文件中的已知属性系列”分为两个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;本文：&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;什么？你的 csproj 文件太长不想看？说明你用了旧格式的 csproj，阅读我的另一篇文章 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj&lt;/a&gt; 将它转为新格式之后，你就会觉得这么简短精炼的 csproj 文件，真不忍将它写杂。&lt;/p&gt;

&lt;p&gt;比如通过以下写法，可以将所有的 *.xaml.cs 文件折叠到对应的 *.xaml 文件下，而不需要像旧 csproj 格式那样每个文件都写一份：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml.cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DependentUpon&amp;gt;&lt;/span&gt;%(Filename)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DependentUpon&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Compile&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编译上下文&quot;&gt;编译上下文&lt;/h2&gt;

&lt;p&gt;以下属性是基本的输出路径属性，可以在 Microsoft.NET.DefaultOutputPaths.targets 找到。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Configuration)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这就是我们传说中决定 Debug 还是 Release 的属性。如果没有指定，默认是 Debug。本身没有什么意义，因为各种其他行为判断了这个属性的值，于是就有了编译差别。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Platform)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;默认是 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnyCPU&lt;/code&gt;，还可以是 &lt;code class=&quot;highlighter-rouge&quot;&gt;x86&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;x64&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;ARM&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseOutputPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;输出路径的起始位置。如果没有指定，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin\&lt;/code&gt;。修改这个属性可以间接修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;OutputPath&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(OutputPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;输出路径，默认有两种可能的值。如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnyCPU&lt;/code&gt; 编译，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseOutputPath)$(Configuration)\&lt;/code&gt;；否则就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseOutputPath)$(PlatformName)\$(Configuration)\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseIntermediateOutputPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;临时生成路径的起始位置。如果没有指定，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;obj\&lt;/code&gt;。修改这个属性可以间接修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;IntermediateOutputPath&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;临时生成路径，默认有两种可能的值。如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;AnyCPU&lt;/code&gt; 编译，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseIntermediateOutputPath)$(Configuration)\&lt;/code&gt;；否则就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(BaseIntermediateOutputPath)$(PlatformName)\$(Configuration)\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，如果你试图在编译期间使用 dll，你可能需要判断运行时环境：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildRuntimeType)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;例如你可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Condition=&quot; '$(MSBuildRuntimeType)' == 'Core'&quot;&lt;/code&gt; 来判断当前编译环境是否是 .NET Core。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于输出路径的更多说明，可以阅读我的另一篇博客：&lt;a href=&quot;/post/the-properties-that-affetcs-project-output-path&quot;&gt;如何更精准地设置 C# / .NET Core 项目的输出路径？（包括添加和删除各种前后缀）&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;以下属性控制哪些文件应该被默认包含在编译中，可以在 Microsoft.NET.TargetFrameworkInference.targets 找到。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(EnableDefaultItems)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;默认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，如果指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，那么就不自动将 .cs 和 .resx 文件引入。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(DefaultItemExcludes)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;默认为输出路径（&lt;code class=&quot;highlighter-rouge&quot;&gt;OutputPath&lt;/code&gt;）和临时生成路径（&lt;code class=&quot;highlighter-rouge&quot;&gt;IntermediateOutputPath&lt;/code&gt;）下的所有文件。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(AppendTargetFrameworkToOutputPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;默认我们生成路径会包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;net47&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;netcoreapp2.1&lt;/code&gt; 这样的一层文件夹，如果指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，这一层文件夹就不会生成了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;下面是 Microsoft.NET.Sdk 中的一部分源码，在 Microsoft.NET.Sdk.DefaultItems.props 文件中，可以发现还有更多与控制自动引入文件相关的属性。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(EnableDefaultItems)' == 'true' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**/*$(DefaultLanguageSourceExtension)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(EnableDefaultCompileItems)' == 'true' &quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;EmbeddedResource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**/*.resx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(EnableDefaultEmbeddedResourceItems)' == 'true' &quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(EnableDefaultItems)' == 'true' And '$(EnableDefaultNoneItems)' == 'true' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**/*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**/*$(DefaultLanguageSourceExtension)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**/*.resx&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;以下属性是 Microsoft.NET.Sdk 中的各种 Target 使用的配置属性，设置这些属性也影响到生成过程。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此程序集的版本，这是很多其他版本号未设置时的默认值。而此值的默认值是 1.0.0 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;3.1.2-beta&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 以下属性是当引用的 dll 出现版本冲突时，用于自动生成绑定重定向的。
         详见：https://www.erikheemskerk.nl/transitive-nuget-dependencies-net-core-got-your-back/ --&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AutoGenerateBindingRedirects&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AutoGenerateBindingRedirects&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GenerateBindingRedirectsOutputType&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GenerateBindingRedirectsOutputType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以阅读 &lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程&lt;/a&gt; 和  &lt;a href=&quot;/post/read-microsoft-net-sdk-en&quot;&gt;Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling&lt;/a&gt; 了解更多 Microsoft.NET.Sdk 源码。&lt;/p&gt;

&lt;h2 id=&quot;开关&quot;&gt;开关&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(AutoGenerateBindingRedirects)&lt;/code&gt; 设置开启或关闭绑定重定向，详情可参考：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection&quot;&gt;Enable or disable autogenerated binding redirects - Microsoft Docs&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;文件路径&quot;&gt;文件路径&lt;/h2&gt;

&lt;h3 id=&quot;项路径&quot;&gt;项路径&lt;/h3&gt;

&lt;p&gt;写在 csproj 文件中 ItemGroup 组中的每一个元素即“项”。&lt;/p&gt;

&lt;p&gt;对以下这一项进行说明的话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;  
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src\Program.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;那么，可用的属性有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(FullPath)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件的完全路径，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\GitHub\Demo\Walterlv.DemoProject\src\Program.cs&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(RootDir)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件所在的根目录，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(Filename)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件名（不含扩展名），例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(Extension)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件扩展名，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;.cs&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(RelativeDir)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件所在的文件夹，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;src\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(Directory)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;除了根目录之外的目录，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv\GitHub\Demo\Walterlv.DemoProject\src\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(RecursiveDir)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;如果项是用通配符写的，那么此值表示匹配到某一项时的目录，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv\GitHub\Demo\Walterlv.DemoProject\src\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(Identity)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;项的标识符，也就是 Include 里写的东西，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;src\Program.cs&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(ModifiedTime)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件的修改时间，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;2018-04-12 21:00:43.7851385&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(CreatedTime)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件的创建时间，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;2018-04-12 21:01:50.1417635&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%(AccessedTime)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文件最近被访问的时间，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;2018-04-12 21:02:15.4132476&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;全局路径&quot;&gt;全局路径&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;项目文件
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectFullPath)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目文件的绝对路径，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\GitHub\Demo\Walterlv.DemoProject.csproj&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectDirectory)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目所在的文件夹，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\GitHub\Demo&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectFile)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目文件的完整名称，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.DemoProject.csproj&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectName)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目文件的名称，不含扩展名，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.DemoProject&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectExtension)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目文件的扩展名，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;.csproj&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectDirectoryNoRoot)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;项目文件去除驱动器的路径，包含反斜杠&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;部件（例如 .props 文件或 .targets 文件，当然也包含 .csproj 文件）
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFileFullPath)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;用这个属性的文件所在的绝对路径，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\.nuget\packages\walterlv.nuget.demo\2.13.0\build\netstandard2.0\Walterlv.NuGet.Demo.targets&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFileDirectory)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此文件所在的文件夹，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\walterlv\.nuget\packages\walterlv.nuget.demo\2.13.0\build\netstandard2.0\&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFile)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此文件的完整名称，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGet.Demo.targets&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFileName)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此文件的名称，不含扩展名，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NuGet.Demo&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFileExtension)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此文件的扩展名，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;.targets&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildThisFileDirectoryNoRoot)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此文件去除驱动器的路径，包含反斜杠&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;环境
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildStartupDirectory)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;启动 MSBuild 时的路径，类似于工作目录（输入 msbuild 命令时所在的那个文件夹）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;工具
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildToolsPath)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;MSBuild 工具所在的路径&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildToolsVersion)&lt;/code&gt;
        &lt;ul&gt;
          &lt;li&gt;此次编译锁使用的工具的版本&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外还有一些在新的 SDK 中几乎不会在日常开发中用到的全局属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;: MSBuild 程序所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildExtensionsPath)&lt;/code&gt;: 自定义 targets 所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildExtensionsPath32)&lt;/code&gt;: 自定义 targets 所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildExtensionsPath64)&lt;/code&gt;: 自定义 targets 所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildLastTaskResult)&lt;/code&gt;: 如果前一个 Task 结束后成功，则为 true；否则为 false&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildNodeCount)&lt;/code&gt;: 编译时并发的进程数，与命令行中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;/maxcpucount&lt;/code&gt; 时一个意思&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProgramFiles32)&lt;/code&gt;: 通常是 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectDefaultTargets)&lt;/code&gt;: 在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Project&lt;/code&gt; 根节点上设置的默认 Targets，例如: &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Project DefaultTargets=&quot;A;B;C&quot; &amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;: MSBuild 程序所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;: MSBuild 程序所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;: MSBuild 程序所在的路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildBinPath)&lt;/code&gt;: MSBuild 程序所在的路径&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果希望了解在 csproj 中创建 NuGet 包时可用的属性，请参考我的另一篇博客：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（知道了这些，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms164313.aspx&quot;&gt;MSBuild Well-known Item Metadata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:36:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/known-properties-in-csproj.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/known-properties-in-csproj.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>让一个 csproj 项目指定多个开发框架</title>
        <description>&lt;p&gt;可移植类库、共享项目、.NET Standard 项目都能够帮我们完成跨多个 .NET SDK 的单一项目开发，但它们的跨 SDK 开发都有些限制。现在，我们又有新的方式能够跨多个 .NET SDK 开发了，这就是使用新的 csproj 文件格式。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;看看拥有多个开发框架的项目长什么样吧！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-12-15-17-26.png&quot; alt=&quot;多 SDK 的项目&quot; /&gt;&lt;br /&gt;
▲ 多 SDK 项目&lt;/p&gt;

&lt;p&gt;这个是我和 &lt;a href=&quot;https://github.com/erdao&quot;&gt;erdao&lt;/a&gt; 在 GitHub 上开源项目 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer&quot;&gt;dotnet-campus/MSTestEnhancer&lt;/a&gt; 的项目依赖截图。是不是很激动？&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;sdk-风格的-csproj-文件&quot;&gt;Sdk 风格的 csproj 文件&lt;/h2&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/organize-csharp-project-targeting-multiple-platforms&quot;&gt;如何组织一个同时面向 UWP/WPF/.Net Core 控制台的 C# 项目解决方案 - walterlv&lt;/a&gt; 一文中我讲了 .NET Standard 的方式，这种方式优势非常明显，跟普通的开发方式一样，也是我最推荐的方式。但缺点是要求目标 SDK 支持对应的 .NET Standard 版本。&lt;/p&gt;

&lt;p&gt;使用&lt;strong&gt;共享项目&lt;/strong&gt;的方式则是直接共享了源码，只要在目标项目中指定了条件编译符，那么源码便能针对各种不同的目标框架进行分别编译。但缺点是对扩展插件的支持较差（可能是因为扩展插件难以判断项目的真实开发框架），而且 Visual Studio 本身对它的支持也有 BUG（例如切换编写文件所属的项目经常会失败）。&lt;/p&gt;

&lt;p&gt;新的 csproj 文件能够指定多个开发框架。这样，我们便能同时编写适用于 .NET Framework 4.5 的和 .NET Standard 2.0 的代码，同时还能够得到 Visual Studio 和扩展插件较好的支持。&lt;/p&gt;

&lt;p&gt;.NET Standard 和 .NET Core 项目在创建之时就已经是新的 csproj 格式了，但 .NET Framework 项目、UWP/WPF 项目依然使用旧风格的 csproj 文件。对于 .NET Framework 项目，可以通过 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt; 一文进行迁移。不过&lt;strong&gt;对于 WPF/UWP 项目，根本就没有跨多个 SDK 的必要，就不要改了&lt;/strong&gt;……&lt;/p&gt;

&lt;p&gt;如果是新开项目——强烈建议先按照 .NET Standard 项目类型建好，再修改成多开发框架。&lt;/p&gt;

&lt;h2 id=&quot;如何指定多个开发框架&quot;&gt;如何指定多个开发框架&lt;/h2&gt;

&lt;p&gt;只要是 Sdk 风格的 csproj 文件，指定多个开发框架真的是相当的简单。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net45;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个文件里的其他内容 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请&lt;strong&gt;特别注意&lt;/strong&gt;！！！&lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 从单数形式变为了复数形式 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt;！！！这个时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 是编译时自动指定的。&lt;/p&gt;

&lt;p&gt;如果是对以上多框架的项目进行单元测试，考虑到编译的目标平台是多个的，单元测试项目也需要指定多个目标框架。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net471;netcoreapp2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsPackable&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsPackable&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这个文件里的其他内容 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;多框架项目的坑以及如何避坑&quot;&gt;多框架项目的坑以及如何避坑&lt;/h2&gt;

&lt;p&gt;微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/how-to-configure-projects-to-target-multiple-platforms?wt.mc_id=MVP&quot;&gt;How to: Configure Projects to Target Multiple Platforms - Microsoft Docs&lt;/a&gt; 中只说了如何指定多个目标框架，并没有提及指定了多框架以后的坑。&lt;/p&gt;

&lt;p&gt;如果多开发框架中包含了低版本的 .NET Framework，例如 4.0/4.5 等，那么这些坑才比较容易凸显——因为这些版本的 .NET Framework 与 .NET Standard 的第三方库差异较大。所以，我们需要有方法来解决其第三方库引用的差异。这时需要在 csproj 文件中指定包含条件。例如：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net471;netcoreapp2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(TargetFramework)'!='netcoreapp2.0'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsPackable&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsPackable&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里的引用是二者共有的 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里的引用用于非 .NET Core 框架 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(TargetFramework)'!='netcoreapp2.0'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xxx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里的引用用于 .NET Core 框架 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(TargetFramework)'=='netcoreapp2.0'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Yyy&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer&quot;&gt;dotnet-campus/MSTestEnhancer&lt;/a&gt; 项目中，只有 .NET Framework 4.5 才需要引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.ValueTuple&lt;/code&gt;，于是加上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;net45&lt;/code&gt; 条件判断：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--EXTERNAL_PROPERTIES: TargetFramework--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(TargetFramework)'=='net45'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.4.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那段注释的作用是告诉代码分析工具 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 是外部属性，上下文环境中找不到这个属性是正常的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ekonbenefits/impromptu-interface/blob/master/ImpromptuInterface/ImpromptuInterface.csproj&quot;&gt;impromptu-interface/ImpromptuInterface.csproj at master · ekonbenefits/impromptu-interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/how-to-configure-projects-to-target-multiple-platforms?wt.mc_id=MVP&quot;&gt;How to: Configure Projects to Target Multiple Platforms - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:36:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/configure-projects-to-target-multiple-platforms.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/configure-projects-to-target-multiple-platforms.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj</title>
        <description>&lt;p&gt;写过 .NET Standard 类库或者 .NET Core 程序的你一定非常喜欢微软为他们新开发的项目文件（对于 C#，则是 csproj 文件）。这种文件非常简洁，组织一个庞大的项目也只需要聊聊二三十行；也非常易读，你可以轻易地修改其代码而不用经过过多的提前学习。当然，微软曾经尝试过用 project.json 来组织项目文件，不过只有短短的预览版阶段用过，此后就废弃了。&lt;/p&gt;

&lt;p&gt;然而组织传统 .NET Framework 类库的 csproj 文件却极其庞大且难以理解。而本文将提供一种迁移方法，帮助你完成这样的迁移，以便体验 Sdk 风格的 csproj 文件带来的诸多好处。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;更新&lt;/strong&gt;：&lt;/p&gt;

&lt;p&gt;感谢小伙伴 &lt;a href=&quot;https://github.com/KodamaSakuno&quot;&gt;KodamaSakuno (神樹桜乃)&lt;/a&gt; 的指导，我们可以有第三方的解决方案 MSBuild.Sdk.Extras 来更简单地完成迁移。阅读 &lt;a href=&quot;/post/use-msbuild-sdk-extras-for-wpf-and-uwp&quot;&gt;MSBuild.Sdk.Extras&lt;/a&gt; 来了解更多。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;sdk-风格的-csproj-文件的优势与直观体验&quot;&gt;Sdk 风格的 csproj 文件的优势与直观体验&lt;/h2&gt;

&lt;p&gt;如果你已经体验过 Sdk 风格的 csproj 文件的好处，那么直接前往下一节即可。没体验过的话就来体验一下吧！&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net471&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Walterlv.Demo.csproj&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ComponentModel.Composition&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是我的一个单元测试项目的 csproj 文件，是不是非常简洁？基于 .NET Framework 4.7.1，引用 MSTest v2，测试 Walterlv.Demo 项目，引用了一个 .NET Framework 类库。&lt;/p&gt;

&lt;p&gt;其依赖的显示也非常简洁：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-15-23-33-41.png&quot; alt=&quot;简洁的依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而传统的 csproj 文件是怎样的呢？&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DefaultTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Configuration&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Debug&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Platform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Platform)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;AnyCPU&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Platform&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectGuid&amp;gt;&lt;/span&gt;{F0E83A94-D65F-492D-AF5B-CC43666FE676}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectGuid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Library&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppDesignerFolder&amp;gt;&lt;/span&gt;Properties&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppDesignerFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;Walterlv.UnitTests.Demo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyName&amp;gt;&lt;/span&gt;Walterlv.UnitTests.Demo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworkVersion&amp;gt;&lt;/span&gt;v4.7.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworkVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FileAlignment&amp;gt;&lt;/span&gt;512&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FileAlignment&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectTypeGuids&amp;gt;&lt;/span&gt;{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectTypeGuids&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStudioVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(VisualStudioVersion)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;15.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStudioVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VSToolsPath&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(VSToolsPath)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VSToolsPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ReferencePath&amp;gt;&lt;/span&gt;$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ReferencePath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsCodedUITest&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsCodedUITest&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TestProjectType&amp;gt;&lt;/span&gt;UnitTest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TestProjectType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuGetPackageImportStamp&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuGetPackageImportStamp&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworkProfile&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;full&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Optimize&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Optimize&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\Debug\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;DEBUG;TRACE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WarningLevel&amp;gt;&lt;/span&gt;4&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WarningLevel&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Configuration)|$(Platform)' == 'Release|AnyCPU' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DebugType&amp;gt;&lt;/span&gt;pdbonly&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DebugType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Optimize&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Optimize&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;bin\Release\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DefineConstants&amp;gt;&lt;/span&gt;TRACE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DefineConstants&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorReport&amp;gt;&lt;/span&gt;prompt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorReport&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WarningLevel&amp;gt;&lt;/span&gt;4&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WarningLevel&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;HintPath&amp;gt;&lt;/span&gt;..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/HintPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reference&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;HintPath&amp;gt;&lt;/span&gt;..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll&lt;span class=&quot;nt&quot;&gt;&amp;lt;/HintPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reference&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ComponentModel.Composition&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Core&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DemoTest.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\AssemblyInfo.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;packages.config&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildToolsPath)\Microsoft.CSharp.targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EnsureNuGetPackageBuildImports&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PrepareForBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;ErrorText&amp;gt;&lt;/span&gt;这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息，请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ErrorText&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props'))&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets'))&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而且，还要搭配一个 packages.config 文件来描述 NuGet。&lt;/p&gt;

&lt;p&gt;从对比中我们就能明显看出 Sdk 风格的 csproj 文件的优势：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;文件小，易读易写&lt;/li&gt;
  &lt;li&gt;在版本管理中更容易解冲突&lt;/li&gt;
  &lt;li&gt;NuGet 包的引用没有路径要求，这意味着开发者可以任意指定 NuGet 包的位置&lt;/li&gt;
  &lt;li&gt;嵌套的引用不需要重复指定（如果 A 引用了 B，B 引用了 C；那么 A 不需要显式引用 C 也能调用到 C）&lt;/li&gt;
  &lt;li&gt;可以一边编辑 csproj 一边打开项目，互不影响&lt;/li&gt;
  &lt;li&gt;可以指定多个开发框架，详见 &lt;a href=&quot;/post/configure-projects-to-target-multiple-platforms&quot;&gt;让一个项目指定多个开发框架 - 吕毅的博客&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;迁移普通-net-framework-类库的项目文件&quot;&gt;迁移普通 .NET Framework 类库的项目文件&lt;/h2&gt;

&lt;p&gt;目前只有基于 .NET Core 和 .NET Standard 的普通项目能够使用这种 Sdk 风格的 csproj 文件。在 GitHub 的讨论（&lt;a href=&quot;https://github.com/dotnet/project-system/issues/1467&quot;&gt;XAML files are not supported · Issue #1467 · dotnet/project-system&lt;/a&gt;）中，.NET Core 的开发者们是这么说的。&lt;/p&gt;

&lt;p&gt;不过，.NET Framework 项目也能够有限地得到支持。具体可支持的类型以及迁移方法我的小伙伴写了一篇博客，请前往此处查看：&lt;a href=&quot;https://lindexi.github.io/lindexi/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html&quot;&gt;从以前的项目格式迁移到 VS2017 新项目格式 - 林德熙&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;目前没有自动的迁移方法，至少在我的实际迁移过程中，只有少数项目能够直接编译通过。由于以上我的小伙伴给出了具体的迁移方法，所以此处我只给出迁移思路。&lt;/p&gt;

&lt;h3 id=&quot;手动迁移&quot;&gt;手动迁移&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;第一步：&lt;/strong&gt;将以下代码复制到原有的 csproj 文件中（不管原来的文件里有多少内容）&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;第二步：&lt;/strong&gt;修改目标 .NET Framework 框架版本号，比如 net45、net462、net472。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第三步：&lt;/strong&gt;安装此前已经安装好的 NuGet 包，或者把原来的 packages.config 文件里的 NuGet 配置复制到 csproj 文件中，并统一修改格式：&lt;/p&gt;

&lt;p&gt;从&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net45&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net45&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;修改成&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;第四步：&lt;/strong&gt;引用此前引用过的类库文件和项目引用&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第五步：&lt;/strong&gt;删除 Properties 文件夹和里面的所有文件，因为这些信息已经被 csproj 文件记录并自动生成了。&lt;/p&gt;

&lt;h3 id=&quot;手动迁移过程中可能遇到的坑&quot;&gt;手动迁移过程中可能遇到的坑&lt;/h3&gt;

&lt;p&gt;如果你的项目比较小，比较新，比较少折腾，那么走完上面的五个步骤基本上你应该能够直接编译通过并运行了。不过，能做到这些的项目其实真不多，基本上或多或少都会遇到一些坑。&lt;/p&gt;

&lt;p&gt;比如，你可能曾经排除出项目之外的文件现在又回来了——现在，你需要重新将他们排除，或者直接删除掉！&lt;/p&gt;

&lt;p&gt;比如，你可能放入项目的不止有 cs 文件，还有其他各种用途的资源——你需要重新选中他们然后在属性面板中设置文件的生成属性。&lt;/p&gt;

&lt;p&gt;比如，你可能有一些 xaml 文件——这时，你需要看本文的下一个章节 &lt;a href=&quot;/post/introduce-new-style-csproj-into-net-framework.html#%E8%BF%81%E7%A7%BB-wpfuwp-%E8%BF%99%E7%B1%BB-xaml-ui-%E7%B1%BB%E5%BA%93%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6&quot;&gt;迁移 WPF/UWP 这类 XAML UI 类库的项目文件&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;自动迁移&quot;&gt;自动迁移&lt;/h3&gt;

&lt;p&gt;自动迁移的方法我写了一篇新的博客，请阅读 &lt;a href=&quot;/post/migrate-packages-config-to-package-reference&quot;&gt;自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference&lt;/a&gt;。当然，目前自动迁移还只是 NuGet 引用方式的改变，加上文件通配符的帮助，我们的 csproj 文件即使依然是旧格式，也能非常简洁。&lt;/p&gt;

&lt;h2 id=&quot;迁移-wpfuwp-这类-xaml-ui-类库的项目文件&quot;&gt;迁移 WPF/UWP 这类 XAML UI 类库的项目文件&lt;/h2&gt;

&lt;p&gt;UWP 项目已经是 .NET Core 了，然而它依然还在采用旧样式的 csproj 文件，这让人感到不可思议。然而我并不知道是否是因为旧版本的 Visual Studio 2017 不支持在 Sdk 风格的 csproj 中编译 XAML。&lt;/p&gt;

&lt;p&gt;包含 XAML 的 WPF/UWP 项目需要额外添加以下至少三个节点（&lt;code class=&quot;highlighter-rouge&quot;&gt;LanguageTargets&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Page.Generator&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Compile.DependentUpon&lt;/code&gt;）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;LanguageTargets&amp;gt;&lt;/span&gt;$(MSBuildToolsPath)\Microsoft.CSharp.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LanguageTargets&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DependentUpon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(Filename)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Designer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild:Compile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果这只是一个简单的 WPF/UWP 类库，那么这些节点其实就足够了。不过，如果这是一个启动项目（&lt;code class=&quot;highlighter-rouge&quot;&gt;exe&lt;/code&gt;），那么还需要添加应用程序定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationDefinition&lt;/code&gt; 和其他启动属性。于是，整个 csproj 文件看起来是这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolsVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;15.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LanguageTargets&amp;gt;&lt;/span&gt;$(MSBuildToolsPath)\Microsoft.CSharp.targets&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LanguageTargets&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果没有跨平台要求，且想去掉控制台窗口，则设为 WinExe --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;OutputType&amp;gt;WinExe&amp;lt;/OutputType&amp;gt; --&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 设置为 App.xaml 的类名（含命名空间） --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StartupObject&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- App.xaml --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ApplicationDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Designer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild:Compile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- XAML elements --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;App.xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Designer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSBuild:Compile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.xaml.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DependentUpon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%(Filename)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Resources --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EmbeddedResource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Resources.resx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResXFileCodeGenerator&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;LastGenOutput=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Resources.Designer.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Resources.Designer.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AutoGen=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DependentUpon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Resources.resx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DesignTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Settings --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Settings.settings&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Generator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SettingsSingleFileGenerator&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;LastGenOutput=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Settings.Designer.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Settings.Designer.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AutoGen=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DependentUpon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Settings.settings&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PresentationCore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PresentationFramework&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Xaml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowsBase&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;OutputType /&amp;gt;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;StartupObject /&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ApplicationDefinition /&amp;gt;&lt;/code&gt; 如果是类库则需要去掉。&lt;/p&gt;

&lt;p&gt;特别注意！你 &lt;strong&gt;必须将 Visual Studio 升级到 15.8 以上的版本&lt;/strong&gt;，否则WPF 或者 UWP 项目迁移成新项目之后，默认新建的 XAML 文件会不可见，每次都需要手工去 csproj 中删掉自动增加的错误的 XAML 编译类型。&lt;/p&gt;

&lt;h3 id=&quot;迁移中各种诡异的报错及其解决方法&quot;&gt;迁移中各种诡异的报错及其解决方法&lt;/h3&gt;

&lt;p&gt;对于带 XAML 的项目，如果在迁移过程中放弃了，试图恢复成原来的方案，那么在编译时会发生一个诡异的错误：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Your project.json doesn’t have a runtimes section. You should add ‘“runtimes”: { “win”: { } }’ to your project.json and then re-run NuGet restore.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-15-13-05-29.png&quot; alt=&quot;错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-16-10-49-49.png&quot; alt=&quot;错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;就是试图迁移的那个项目！无论依赖了谁还是被谁依赖，都是此项目发生“NuGet”错误。&lt;/p&gt;

&lt;p&gt;其实这是只有新的项目文件才会出现的编译错误，而错误原因是 NuGet 的缓存文件中与包引用相关的信息已经不正确了，需要运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget restore&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; 重新更新此文件才行。但是，只有使用了 Sdk 风格的 csproj 文件才会在执行了此命令后重新生成正确的包引用缓存文件；原来的格式并不会生成此文件，也就是说，无法修复。&lt;/p&gt;

&lt;p&gt;唯一的解决办法就是清除项目中的所有 NuGet 缓存，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git clean -xdf&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;迁移之后的劣势&quot;&gt;迁移之后的劣势&lt;/h2&gt;

&lt;p&gt;迁移成新的 csproj 格式之后，新格式中不支持的配置会丢失。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;ProjectTypeGuid&lt;/strong&gt; 这个属性标志着此项目的类型，比如指定为 WPF 自定义控件库的项目新建文件的模板有自定义控件，而普通类库则不会有。&lt;/li&gt;
  &lt;li&gt;特别注意！WPF 或者 UWP 项目迁移成新项目之后，默认新建的 XAML 文件会不可见，每次都需要手工去 csproj 中删掉自动增加的错误的 XAML 编译类型。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;什么都不用管的第三方迁移方案&quot;&gt;什么都不用管的第三方迁移方案&lt;/h2&gt;

&lt;p&gt;感谢小伙伴 &lt;a href=&quot;https://github.com/KodamaSakuno&quot;&gt;KodamaSakuno (神樹桜乃)&lt;/a&gt; 的指导，我们可以有第三方的解决方案 MSBuild.Sdk.Extras 来更简单地完成迁移。阅读 &lt;a href=&quot;/post/use-msbuild-sdk-extras-for-wpf-and-uwp&quot;&gt;MSBuild.Sdk.Extras&lt;/a&gt; 来了解更多。相比于以上全文的迁移以及带来的劣势，第三方方案并没有发现明显的缺陷，推荐使用！&lt;/p&gt;

&lt;h2 id=&quot;什么都不用改的微软官方迁移方案&quot;&gt;什么都不用改的微软官方迁移方案&lt;/h2&gt;

&lt;p&gt;在 csproj 前面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Sdk=&quot;Microsoft.NET.Sdk.WindowsDesktop&quot;&lt;/code&gt; 使用 .NET Core 3 为我们带来的原生支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/sdk/issues/810&quot;&gt;XAML files are not supported · Issue #810 · dotnet/sdk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/project-system/issues/1467&quot;&gt;XAML files are not supported · Issue #1467 · dotnet/project-system&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.natemcmaster.com/blog/2017/03/09/vs2015-to-vs2017-upgrade/&quot;&gt;Old csproj to new csproj: Visual Studio 2017 upgrade guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/msbuild/issues/1688&quot;&gt;Using the new .Csproj without .Net core · Issue #1688 · Microsoft/msbuild&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/44140673/6233938&quot;&gt;c# - WPF App Using new csproj format - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/project-system/issues/1467&quot;&gt;XAML files are not supported · Issue #1467 · dotnet/project-system&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/sdk/issues/810&quot;&gt;XAML files are not supported · Issue #810 · dotnet/sdk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/50550063/6233938&quot;&gt;c# - How-to migrate Wpf projects to the new VS2017 format - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/vsts-tasks/issues/5931&quot;&gt;project.json doesn’t have a runtimes section, add ‘“runtimes”: { “win”: { } }’ to project.json · Issue #5931 · Microsoft/vsts-tasks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/msbuild/issues/394&quot;&gt;Ignore PROJECT.JSON when using .CSPROJ · Issue #394 · Microsoft/msbuild&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/cli/issues/6294&quot;&gt;dotnet build fails when referencing a project converted to PackageReference · Issue #6294 · dotnet/cli&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/45614394/6233938&quot;&gt;Visual studio project.json does not have a runtime section - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 01:35:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/introduce-new-style-csproj-into-net-framework.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/introduce-new-style-csproj-into-net-framework.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>.NET Core/Framework 创建委托以大幅度提高反射调用的性能</title>
        <description>&lt;p&gt;都知道反射伤性能，但不得不反射的时候又怎么办呢？当真的被问题逼迫的时候还是能找到解决办法的。&lt;/p&gt;

&lt;p&gt;为反射得到的方法创建一个委托，此后调用此委托将能够提高近乎直接调用方法本身的性能。（当然 Emit 也能够帮助我们显著提升性能，不过直接得到可以调用的委托不是更加方便吗？）&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;性能对比数据&quot;&gt;性能对比数据&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-07-16-03-57.png&quot; alt=&quot;性能对比数据&quot; /&gt;&lt;br /&gt;
▲ 没有什么能够比数据更有说服力（注意后面两行是有秒数的）&lt;/p&gt;

&lt;p&gt;可能我还需要解释一下那五行数据的含义：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;直接调用（😏&lt;em&gt;应该没有什么比直接调用函数本身更有性能优势的吧&lt;/em&gt;）&lt;/li&gt;
  &lt;li&gt;做一个跟直接调用的方法功能一模一样的委托（😮&lt;em&gt;目的是看看调用委托相比调用方法本身是否有性能损失，从数据上看，损失非常小&lt;/em&gt;）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;本文重点&lt;/strong&gt; 将反射出来的方法创建一个委托，然后调用这个委托（🤩&lt;em&gt;看看吧，性能跟直接调差别也不大嘛&lt;/em&gt;）&lt;/li&gt;
  &lt;li&gt;先反射得到方法，然后一直调用这个方法（😥&lt;em&gt;终于可以看出来反射本身还是挺伤性能的了，50 多倍的性能损失啊&lt;/em&gt;）&lt;/li&gt;
  &lt;li&gt;缓存都不用，从头开始反射然后调用得到的方法（😒&lt;em&gt;100 多倍的性能损失了&lt;/em&gt;）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以下是测试代码，可以更好地理解上图数据的含义：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 调用的目标实例。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StubClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 使用反射找到的方法。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StubClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StubClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 将反射找到的方法创建一个委托。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstanceMethodBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstanceMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 跟被测方法功能一样的纯委托。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pureFunc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 测试次数。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 直接调用。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 次 - 直接调用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 使用同样功能的 Func 调用。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pureFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 次 - 使用同样功能的 Func 调用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 使用反射创建出来的委托调用。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 次 - 使用反射创建出来的委托调用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 使用反射得到的方法缓存调用。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 次 - 使用反射得到的方法缓存调用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 直接使用反射调用。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StubClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StubClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 次 - 直接使用反射调用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StubClass&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的代码中，有一个我们还没有实现的 &lt;code class=&quot;highlighter-rouge&quot;&gt;InstanceMethodBuilder&lt;/code&gt; 类型，接下来将介绍如何实现它。&lt;/p&gt;

&lt;h2 id=&quot;如何实现&quot;&gt;如何实现&lt;/h2&gt;

&lt;p&gt;实现的关键就在于 &lt;code class=&quot;highlighter-rouge&quot;&gt;MethodInfo.CreateDelegate&lt;/code&gt; 方法。这是 .NET Standard 中就有的方法，这意味着 .NET Framework 和 .NET Core 中都可以使用。&lt;/p&gt;

&lt;p&gt;此方法有两个重载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;要求传入一个类型，而这个类型就是应该转成的委托的类型&lt;/li&gt;
  &lt;li&gt;要求传入一个类型和一个实例，一样的，类型是应该转成的委托的类型&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;他们的区别在于前者创建出来的委托是直接调用那个实例方法本身，后者则更原始一些，真正调用的时候还需要传入一个实例对象。&lt;/p&gt;

&lt;p&gt;拿上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;StubClass&lt;/code&gt; 来说明会更直观一些：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StubClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前者得到的委托相当于 &lt;code class=&quot;highlighter-rouge&quot;&gt;int Test(int i)&lt;/code&gt; 方法，后者得到的委托相当于 &lt;code class=&quot;highlighter-rouge&quot;&gt;int Test(StubClass instance, int i)&lt;/code&gt; 方法。（在 IL 里实例的方法其实都是后者，而前者更像 C# 中的代码，容易理解。）&lt;/p&gt;

&lt;p&gt;单独使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateDelegate&lt;/code&gt; 方法可能每次都需要尝试第一个参数到底应该传入些什么，于是我将其封装成了泛型版本，增加易用性。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics.Contracts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InstanceMethodBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 调用时就像 var result = func(t)。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateInstanceMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 调用时就像 var result = func(this, t)。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TInstanceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TReturnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;泛型的多参数版本可以使用泛型类型生成器生成，我在 &lt;a href=&quot;/post/generate-code-of-generic-types&quot;&gt;生成代码，从 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;T&amp;gt;&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;T1, T2, Tn&amp;gt;&lt;/code&gt; —— 自动生成多个类型的泛型 - 吕毅&lt;/a&gt; 一文中写了一个泛型生成器，可以稍加修改以便适应这种泛型类。&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Apr 2019 11:21:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-delegate-to-improve-reflection-performance.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-delegate-to-improve-reflection-performance.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>将 C++/WinRT 中的线程切换体验带到 C# 中来（WPF 版本）</title>
        <description>&lt;p&gt;如果你要在 WPF 程序中使用线程池完成一个特殊的任务，那么使用 .NET 的 API &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 并传入一个 Lambda 表达式可以完成。不过，使用 Lambda 表达式会带来变量捕获的一些问题，比如说你需要区分一个变量作用于是在 Lambda 表达式中，还是当前上下文全局（被 Lambda 表达式捕获到的变量）。然后，在静态分析的时候，也难以知道此 Lambda 表达式在整个方法中的执行先后顺序，不利于分析潜在的 Bug。&lt;/p&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字编写异步代码的时候，虽然说实质上也是捕获变量，但这时没有显式写一个 Lambda 表达式，所有的变量都是被隐式捕获的变量，写起来就像在一个同步方法一样，便于理解。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;cwinrt&quot;&gt;C++/WinRT&lt;/h2&gt;

&lt;p&gt;以下 C++/WinRT 的代码来自 Raymond Chen 的示例代码。Raymond Chen 写了一个 UWP 的版本用于模仿 C++/WinRT 的线程切换效果。在看他编写的 UWP 版本之前我也思考了可以如何实现一个 .NET / WPF 的版本，然后成功做出了这样的效果。&lt;/p&gt;

&lt;p&gt;Raymond Chen 的版本可以参见：&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190328-00/?p=102368&quot;&gt;C++/WinRT envy: Bringing thread switching tasks to C# (UWP edition) - The Old New Thing&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fire_and_forget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyPage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// We start on a UI thread.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lifetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_strong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Get the control's value from the UI thread.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Move to a background thread.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resume_background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Do the computation on a background thread.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Compute1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContactWebServiceAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Compute2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Return to the UI thread to provide an interim update.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resume_foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Back on the UI thread: We can update UI elements.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;TextBlock1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;TextBlock2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Back to the background thread to do more computations.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resume_background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetExtraDataAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Compute3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Return to the UI thread to provide a final update.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resume_foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Update the UI one last time.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;TextBlock3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;co_await winrt::resume_background();&lt;/code&gt; 可以将线程切换至线程池，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;co_await winrt::resume_foreground(Dispatcher());&lt;/code&gt; 可以将线程切换至 UI。&lt;/p&gt;

&lt;p&gt;也许你会觉得这样没什么好处，因为 C#/.NET 的版本里面 Lambda 表达式一样可以这么做：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里的代码会在线程池执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里的代码会回到 UI 线程执行。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，现在我们给出这样的写法：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 仅在某些特定的情况下才使用线程池执行，而其他情况依然在主线程执行 DoSomething()。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;co_await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resume_background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你就会发现 Lambda 的版本变得很不好理解了。&lt;/p&gt;

&lt;h2 id=&quot;c--net--wpf-版本&quot;&gt;C# / .NET / WPF 版本&lt;/h2&gt;

&lt;p&gt;我们现在编写一个自己的 Awaiter 来实现这样的线程上下文切换。&lt;/p&gt;

&lt;p&gt;关于如何编写一个 Awaiter，可以阅读我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？ - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？ - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里，我直接贴出我编写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSwitcher&lt;/code&gt; 类的全部源码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.ThreadSwitchingTasks&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherSwitcher&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPoolAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeBackground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThreadPoolAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPoolAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeBackground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThreadPoolAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeForeground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadPoolAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPoolAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Raymond Chen 取的类名是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadSwitcher&lt;/code&gt;，不过我认为可能 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 在 WPF 中更能体现其线程切换的含义。&lt;/p&gt;

&lt;p&gt;于是，我们来做一个试验。以下代码在 MainWindow.xaml.cs 里面，如果你使用 Visual Studio 创建一个 WPF 的空项目的话是可以找到的。随便放一个 Button 添加事件处理函数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DemoButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManagedThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResumeBackground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManagedThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResumeForeground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManagedThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;id0 和 id2 在主线程上，id1 是线程池中的一个线程。&lt;/p&gt;

&lt;p&gt;这样，我们便可以在一个上下文中进行线程切换了，而不需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 通过一个 Lambda 表达式来完成这样的任务。&lt;/p&gt;

&lt;p&gt;现在，这种按照某些特定条件才切换到后台线程执行的代码就很容易写出来了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 仅在某些特定的情况下才使用线程池执行，而其他情况依然在主线程执行 DoSomething()。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResumeBackground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;raymond-chen-的版本&quot;&gt;Raymond Chen 的版本&lt;/h2&gt;

&lt;p&gt;Raymond Chen 后来在另一篇博客中也编写了一份 WPF / Windows Forms 的线程切换版本。请点击下方的链接跳转至原文阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190329-00/?p=102373&quot;&gt;C++/WinRT envy: Bringing thread switching tasks to C# (WPF and WinForms edition) - The Old New Thing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我在为他的代码添加了所有的注释后，贴在了下面：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.CompilerServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Forms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows.Threading&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供类似于 WinRT 中的线程切换体验。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// https://devblogs.microsoft.com/oldnewthing/20190329-00/?p=102373&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// https://blog.walterlv.com/post/bring-thread-switching-tasks-to-csharp-for-wpf.html&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadSwitcher&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 将当前的异步等待上下文切换到 WPF 的 UI 线程中继续执行。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;dispatcher&quot;&amp;gt;WPF 一个 UI 线程的调度器。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;一个可等待对象，使用 await 等待此对象可以使后续任务切换到 UI 线程执行。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeForegroundAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherThreadSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 将当前的异步等待上下文切换到 Windows Forms 的 UI 线程中继续执行。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;control&quot;&amp;gt;Windows Forms 的一个控件。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;一个可等待对象，使用 await 等待此对象可以使后续任务切换到 UI 线程执行。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControlThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeForegroundAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Control&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ControlThreadSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 将当前的异步等待上下文切换到线程池中继续执行。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;一个可等待对象，使用 await 等待此对象可以使后续的任务切换到线程池执行。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPoolThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ResumeBackgroundAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThreadPoolThreadSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供一个可切换到 WPF 的 UI 线程执行上下文的可等待对象。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherThreadSwitcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherThreadSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当使用 await 关键字异步等待此对象时，将调用此方法返回一个可等待对象。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个值，该值指示是否已完成线程池到 WPF UI 线程的切换。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 由于进行线程的上下文切换必须使用 await 关键字，所以不支持调用同步的 &amp;lt;see cref=&quot;GetResult&quot;/&amp;gt; 方法。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当异步状态机中的前一个任务结束后，将调用此方法继续下一个任务。在此可等待对象中，指的是切换到 WPF 的 UI 线程。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;continuation&quot;&amp;gt;将异步状态机推进到下一个异步状态。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供一个可切换到 Windows Forms 的 UI 线程执行上下文的可等待对象。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ControlThreadSwitcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ControlThreadSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Control&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_control&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当使用 await 关键字异步等待此对象时，将调用此方法返回一个可等待对象。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControlThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个值，该值指示是否已完成线程池到 Windows Forms UI 线程的切换。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvokeRequired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 由于进行线程的上下文切换必须使用 await 关键字，所以不支持调用同步的 &amp;lt;see cref=&quot;GetResult&quot;/&amp;gt; 方法。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当异步状态机中的前一个任务结束后，将调用此方法继续下一个任务。在此可等待对象中，指的是切换到 Windows Forms 的 UI 线程。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;continuation&quot;&amp;gt;将异步状态机推进到下一个异步状态。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Control&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 提供一个可切换到线程池执行上下文的可等待对象。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadPoolThreadSwitcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当使用 await 关键字异步等待此对象时，将调用此方法返回一个可等待对象。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPoolThreadSwitcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个值，该值指示是否已完成 UI 线程到线程池的切换。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 由于进行线程的上下文切换必须使用 await 关键字，所以不支持调用同步的 &amp;lt;see cref=&quot;GetResult&quot;/&amp;gt; 方法。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 当异步状态机中的前一个任务结束后，将调用此方法继续下一个任务。在此可等待对象中，指的是切换到线程池中。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;continuation&quot;&amp;gt;将异步状态机推进到下一个异步状态。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;QueueUserWorkItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190328-00/?p=102368&quot;&gt;C++/WinRT envy: Bringing thread switching tasks to C# (UWP edition) - The Old New Thing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190329-00/?p=102373&quot;&gt;C++/WinRT envy: Bringing thread switching tasks to C# (WPF and WinForms edition) - The Old New Thing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 30 Mar 2019 01:13:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/bring-thread-switching-tasks-to-csharp-for-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/bring-thread-switching-tasks-to-csharp-for-wpf.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>解决 Git 重命名时遇到的大小写不敏感的问题</title>
        <description>&lt;p&gt;Windows/Mac OS 操作系统文件的大小写是不敏感的，不管文件路径是何种奇怪的大小写，我们始终可以以另一种大小写的方式访问到这个路径种的文件或者文件夹。Linux 操作系统文件的大小写却是敏感的，不同大小写意味着不同的路径。于是，Windows 下的 A 文件在 Docs 文件夹下，B 文件在 docs 文件夹下，最终效果是 A B 都在 docs 文件夹下；而同样的情况放到 Linux 中，A B 就在两个不同的文件夹。&lt;/p&gt;

&lt;p&gt;Git 是大小写不敏感的，导致跨操作系统共享的 Git 仓库就会遇到上面的情况。如果重命名的文件或文件夹只有大小写不同，那么对 Git  来说甚至都没有变化。阅读本文将解决 Git 大小写不敏感导致的重命名无效的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;更新：&lt;/strong&gt;Windows 10 1803 更新已经可以支持区分大小写的文件夹了，于是此问题迎刃而解，后面会详细说明。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决方法推荐&quot;&gt;解决方法（推荐）&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;更新 Windows 10 1803 并安装 Windows 的 Linux 子系统；&lt;/li&gt;
  &lt;li&gt;开启文件夹的大小写敏感特性，请参见：&lt;/li&gt;
  &lt;li&gt;使用以下命令改名，可以仅大小写不同，你可能需要加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;-f&lt;/code&gt;，这样即使目标文件名存在，也可以完成改名。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./Docs ./docs
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;改名&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 git 中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mv&lt;/code&gt; 命令的时候，会自动 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 所以是不用额外在敲 &lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt; 的，而后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;commit&lt;/code&gt; 之后就形成了一个提交，之后你该怎么处理你的 git 仓库就怎么处理你的 git 仓库。&lt;/p&gt;

&lt;p&gt;注意，即便你已经开启了文件夹大小写敏感，但你依然需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git mv -f&lt;/code&gt; 命令来重命名，如果使用 Windows 资源管理器来进行重命名，那么 git 也是无法识别到这种仅有大小写名称改变的重命名的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我是分割线&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你不了解到底是怎样的 git 重命名问题，那么可以阅读本博客的原始版本，还是踩了不少坑的。&lt;/p&gt;

&lt;h2 id=&quot;让人困扰的大小写问题&quot;&gt;让人困扰的大小写问题&lt;/h2&gt;

&lt;p&gt;让我对此问题产生困扰的是下面这张图，&lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt; 两个文件夹分开了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-15-42-24.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 分离的两个文件夹&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 改名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt;，于是只有新增的文件才在 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt; 文件夹下，旧文件依然在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 中。&lt;code class=&quot;highlighter-rouge&quot;&gt;README.md&lt;/code&gt; 中的链接可就遭殃了，还要注意大小写！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-16-10-51.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 稍不注意，就 404 了&lt;/p&gt;

&lt;h2 id=&quot;走的弯路&quot;&gt;走的弯路&lt;/h2&gt;

&lt;p&gt;这种问题怎么看都不像是我一个人会遇到的问题，堆栈网上讨论肯定很多。至少截至本文发表时，&lt;a href=&quot;https://stackoverflow.com/questions/17683458/how-do-i-commit-case-sensitive-only-filename-changes-in-git&quot;&gt;How do I commit case-sensitive only filename changes in Git?&lt;/a&gt; 中问题已经得到了 600+ 个赞，回答累计得到 1400+ 个赞了……&lt;/p&gt;

&lt;p&gt;里面探讨的方法归结起来两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;git mv -f OldFileNameCase newfilenamecase&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;git config core.ignorecase false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;尝试方法二coreignorecase-false&quot;&gt;尝试方法二：core.ignorecase false&lt;/h3&gt;

&lt;p&gt;第二种方法看起来更简单，于是我第一时间在我的全局 git 配置文件（&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\.gitconfig&lt;/code&gt;）中添加了一项：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[core]&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;ignorecase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，&lt;code class=&quot;highlighter-rouge&quot;&gt;git status&lt;/code&gt; 就能发现我的 git 仓库中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 文件夹下的所有文件已经标记为修改了，都变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt;，于是愉快的提交推送：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Docs 文件夹改名为 docs 文件夹&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而去其他系统上看——居然有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt; 两份文件夹！！！而且比之前更严重，这一次可是里面的文件都完全重复了一份啊！！！&lt;/p&gt;

&lt;p&gt;这时注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt; 时，其实文件都是“新增”的，并不是“重命名”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-16-32-35.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看来需要使用第一种方法了。&lt;/p&gt;

&lt;h3 id=&quot;尝试方法一mv&quot;&gt;尝试方法一：mv&lt;/h3&gt;

&lt;p&gt;我写下命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./Docs ./docs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行……推送后最终效果居然和第一种方法一样！依然是有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Docs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt; 两份文件夹。&lt;/p&gt;

&lt;h2 id=&quot;尝试出的可行的方法&quot;&gt;尝试出的可行的方法&lt;/h2&gt;

&lt;p&gt;这是堆栈网那位只有 70+ 赞的方法的改进版本。先将文件夹重命名为临时文件夹，然后再从临时文件夹恢复成正常文件夹。&lt;/p&gt;

&lt;p&gt;但是（划重点）&lt;strong&gt;中间需要先 commit 一次，否则和前面的方法效果一样，会存在两份文件夹！&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; ./Docs ./docs.bak
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;改名（第 1/2 步）&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; ./docs.bak/ ./docs
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;改名（第 2/2 步）&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;中间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt; 其实是可以不需要的，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;mv&lt;/code&gt; 命令会自动将修改加入暂存区。&lt;/p&gt;

&lt;p&gt;至此，文件夹才真的做了仅大小写的改名。&lt;/p&gt;

&lt;h2 id=&quot;使用-windows-10-四月更新的特性推荐&quot;&gt;使用 Windows 10 四月更新的特性（推荐）&lt;/h2&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/case-sensitive-in-windows-file-system&quot;&gt;Windows 10 四月更新，文件夹名称也能区分大小写？&lt;/a&gt; 一文中提到可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;fsutil.exe file SetCaseSensitiveInfo&lt;/code&gt; 使某个特定的文件夹支持区分大小写。&lt;/p&gt;

&lt;p&gt;经过尝试，使用此方法后，git 能够支持一次提交完美解决仅大小写的文件夹改名问题，完全不用管 git 的某种配置或其他任何因素。&lt;/p&gt;

&lt;p&gt;首先，使用管理员权限在当前文件夹启动 PowerShell：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-10-43-02.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后允许一下命令，以便开启此文件夹的大小写敏感功能。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fsutil.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetCaseSensitiveInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后，你可以像一般重命名一样去修改文件夹名称，然后像普通提交一样去 git commit。直接能生成一个仅修改文件夹名称大小写的新提交。&lt;/p&gt;

&lt;p&gt;如果你在执行以上命令时出现了问题，请移步至 &lt;a href=&quot;/post/case-sensitive-in-windows-file-system&quot;&gt;Windows 10 四月更新，文件夹名称也能区分大小写？&lt;/a&gt; 统一寻找解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17683458/how-do-i-commit-case-sensitive-only-filename-changes-in-git&quot;&gt;How do I commit case-sensitive only filename changes in Git? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/11183788/6233938&quot;&gt;In a Git repository, how to properly rename a directory? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 29 Mar 2019 09:19:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/case-insensitive-in-git-rename.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/case-insensitive-in-git-rename.html</guid>
        
        
        <category>windows</category>
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>WPF 的命令的自动刷新时机——当你 CanExecute 会返回 true 但命令依旧不可用时可能是这些原因</title>
        <description>&lt;p&gt;在 WPF 中，你可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Command=&quot;{Binding WalterlvCommand}&quot;&lt;/code&gt; 的方式来让 XAML 中的一个按钮或其他控件绑定一个命令。这样，按钮的可用性会自动根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvCommand&lt;/code&gt; 当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanExecute&lt;/code&gt; 的状态来改变。这本是一个非常智能的特性，直到你可能发现你按钮的可用性状态不正确……&lt;/p&gt;

&lt;p&gt;本文介绍默认情况下，WPF 在 UI 上的这些命令会在什么时机进行刷新；以及没有及时刷新时，可以如何强制让这些命令的可用性状态进行刷新。了解了这些，你可能能够解决你在 WPF 程序中命令绑定的一些坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/when-wpf-commands-update-their-states.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/when-wpf-commands-update-their-states-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个最简单的例子&quot;&gt;一个最简单的例子&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TestCommand&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding WalterlvCommand}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略了此命令的初始化。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CanExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 判断命令的可用性。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 省略了执行命令的代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;假如 &lt;code class=&quot;highlighter-rouge&quot;&gt;SomeFlag&lt;/code&gt; 一开始是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，5 秒种后变为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么你会注意到这时的按钮状态并不会刷新。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TestCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，以上所有代码会更像伪代码，如果你不熟悉 WPF，是一定编译不过的。我只是在表达这个意思。&lt;/p&gt;

&lt;h2 id=&quot;如何手动刷新命令&quot;&gt;如何手动刷新命令&lt;/h2&gt;

&lt;p&gt;调用以下代码，即可让 WPF 中的命令刷新其可用性：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateRequerySuggested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;wpf-的命令在何时刷新&quot;&gt;WPF 的命令在何时刷新？&lt;/h2&gt;

&lt;p&gt;默认情况下，WPF 的命令只会在以下时机刷新可用性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;KeyUp&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MouseUp&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GotKeyboardFocus&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LostKeyboardFocus&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用通俗的话来说，就是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;键盘按下的按键抬起的时候&lt;/li&gt;
  &lt;li&gt;在鼠标的左键或者右键松开的时候&lt;/li&gt;
  &lt;li&gt;在任何一个控件获得键盘焦点或者失去键盘焦点的时候&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这部分的代码可以在这里查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Command/CommandDevice.cs,e56c8b8276e9745a,references&quot;&gt;CommandDevice.PostProcessInput&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最关键的代码贴在这里：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 省略前面。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyUpEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MouseUpEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GotKeyboardFocusEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LostKeyboardFocusEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateRequerySuggested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，并不是只在这些时机进行刷新，还有其他的时机，比如这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Menu&lt;/code&gt; 菜单的子菜单项打开的时候（参见 &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/MenuItem.cs,f6b031dd8baedf62,references&quot;&gt;MenuItem.OnIsSubmenuOpenChanged&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;在长按滚动条中的按钮以连续滚动的过程中（参见 &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/Track.cs,e17c022746f4de8b,references&quot;&gt;Tracker.DecreaseRepeatButton&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataGridCell&lt;/code&gt; 的只读属性改变的时候（参见 &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGridCell.cs,561c6f5a5beaebd0,references&quot;&gt;DataGridCell.OnNotifyIsReadOnlyChanged&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataGrid&lt;/code&gt; 中的各种各样的操作中（参见 &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGrid.cs,0a7919e43781659b,references&quot;&gt;DataGrid&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;JournalNavigationScope&lt;/code&gt; 向后导航的时候（参见 &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/AppModel/JournalNavigationScope.cs,279da0f5dea085dc,references&quot;&gt;JournalNavigationScope.OnBackForwardStateChange&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;还有其他，你可以在此链接双击 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidateRequerySuggested&lt;/code&gt; 查看：&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Command/CommandManager.cs,fb01095b2fe73140,references&quot;&gt;InvalidateRequerySuggested&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 29 Mar 2019 08:43:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/when-wpf-commands-update-their-states.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/when-wpf-commands-update-their-states.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>When WPF Commands update their CanExecute states?</title>
        <description>&lt;p&gt;When writing &lt;code class=&quot;highlighter-rouge&quot;&gt;Command=&quot;{Binding WalterlvCommand}&quot;&lt;/code&gt; into your XAML code and your button or other controls can automatically execute command and updating the command states, such as enabling or disabling the button.&lt;/p&gt;

&lt;p&gt;We’ll talk about when the UI commands will refresh their can-execute states and how to force updating the states.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/when-wpf-commands-update-their-states.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/when-wpf-commands-update-their-states-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;This post is written for my Stack Overflow answer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/55348322/6233938&quot;&gt;Why C# WPF button binding command won’t change view after using simple injector? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;a-simple-sample&quot;&gt;A simple sample&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TestCommand&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding WalterlvCommand}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Assume that I've initialized this command.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCommand&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CanExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Return the real can execution state.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// The actual executing procedure.&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See this code below. After 5 seconds, the button will still be disabled even that we set the &lt;code class=&quot;highlighter-rouge&quot;&gt;SomeFlat&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TestCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeFlag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-to-update-manually&quot;&gt;How to update manually?&lt;/h2&gt;

&lt;p&gt;Call this method after you want to update your command states if it won’t update:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateRequerySuggested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;when-do-the-commands-update-their-states&quot;&gt;When do the commands update their states?&lt;/h2&gt;

&lt;p&gt;Commands only update when these general events happen:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;KeyUp&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MouseUp&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GotKeyboardFocus&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LostKeyboardFocus&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the code here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Command/CommandDevice.cs,e56c8b8276e9745a,references&quot;&gt;CommandDevice.PostProcessInput&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the key code is here:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyUpEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MouseUpEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GotKeyboardFocusEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StagingItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoutedEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LostKeyboardFocusEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvalidateRequerySuggested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Actually, not only those events above but also these methods below refresh the command states:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When opening a submenu of a MenuItem. &lt;em&gt;See &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/MenuItem.cs,f6b031dd8baedf62,references&quot;&gt;MenuItem.OnIsSubmenuOpenChanged&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;When pressing and holding a RepeatButton in a Tracker. &lt;em&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/Track.cs,e17c022746f4de8b,references&quot;&gt;Tracker.DecreaseRepeatButton&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;When change the readonly property of &lt;code class=&quot;highlighter-rouge&quot;&gt;DataGridCell&lt;/code&gt;. &lt;em&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGridCell.cs,561c6f5a5beaebd0,references&quot;&gt;DataGridCell.OnNotifyIsReadOnlyChanged&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;When doing many operations in a &lt;code class=&quot;highlighter-rouge&quot;&gt;DataGrid&lt;/code&gt;. &lt;em&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGrid.cs,0a7919e43781659b,references&quot;&gt;DataGrid&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;When navigating back in a &lt;code class=&quot;highlighter-rouge&quot;&gt;JournalNavigationScope&lt;/code&gt;. &lt;em&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/AppModel/JournalNavigationScope.cs,279da0f5dea085dc,references&quot;&gt;JournalNavigationScope.OnBackForwardStateChange&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;And others, you can find references of &lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidateRequerySuggested&lt;/code&gt;: &lt;a href=&quot;https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Command/CommandManager.cs,fb01095b2fe73140,references&quot;&gt;InvalidateRequerySuggested&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 29 Mar 2019 08:43:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/when-wpf-commands-update-their-states-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/when-wpf-commands-update-their-states-en.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>程序员与英语：即时聊天中的英语缩写 lol / lmao / idk</title>
        <description>&lt;p&gt;经常混迹各大英文开发者社区的你，是否会遇到一些奇怪的英文缩写呢？本文整理一些即时聊天中常用的缩写。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;英语部分&quot;&gt;英语部分&lt;/h2&gt;

&lt;h4 id=&quot;lol&quot;&gt;lol&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Laughing out Loud&lt;/li&gt;
  &lt;li&gt;Laugh out Loud&lt;/li&gt;
  &lt;li&gt;Lots of Laughs&lt;/li&gt;
  &lt;li&gt;Laugh Online&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;缩写可以说来源于上面那些，似乎意思是“好好笑啊”。然而事实可能并不是这样……&lt;/p&gt;

&lt;p&gt;不知是否用“呵呵”，“Interesting” 来回复对方的句子呢？嗯嗯，差不多就是这个感觉啦。其实就只是回复一下而已，不要当真。&lt;/p&gt;

&lt;p&gt;或者，这种感觉可能和 233 很像。&lt;/p&gt;

&lt;h4 id=&quot;lmao&quot;&gt;lmao&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Laughing my ass off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;笑到屁屁都掉了。似乎这是一个比 lol 笑得更厉害的笑法儿。&lt;/p&gt;

&lt;h4 id=&quot;idk&quot;&gt;idk&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;I don’t know，我不知道&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;thx--tks&quot;&gt;thx / tks&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Thanks&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;bbs--brb&quot;&gt;bbs / brb&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Be back soon，马上回来&lt;/li&gt;
  &lt;li&gt;Be right back，马上回来&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;btw&quot;&gt;btw&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;By the way，顺便说一下&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;fyi&quot;&gt;FYI&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;For your information，给你信息，你参考一下&lt;/li&gt;
  &lt;li&gt;邮件中会经常见到这个，通常在转发的时候带上&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;ttyl&quot;&gt;ttyl&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Talk to you later，回头再聊&lt;/li&gt;
  &lt;li&gt;这个注意可能说出来之后会有结束当前会话的意味，也许没有回头了&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;歪楼中文部分&quot;&gt;歪楼：中文部分&lt;/h2&gt;

&lt;p&gt;要歪个楼，因为发现中文中也有一些英文中可以对应的说法。&lt;/p&gt;

&lt;h4 id=&quot;3q--3q&quot;&gt;3q / 3Q&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Thanks，谢了&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;1&quot;&gt;1&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;如果是一则消息，表示已读；如果是一个问题，表示“是”“同意”等&lt;/li&gt;
  &lt;li&gt;就一个数字，按下即可回车，很快&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;0&quot;&gt;0&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;如果是一则消息，表示反对；如果是一个问题，表示“否”“不同意”等&lt;/li&gt;
  &lt;li&gt;就一个数字，按下即可回车，很快&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;233&quot;&gt;233&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;愿意是“大笑”，但其实含义已经改变，现在意思几乎跟“呵呵”一样&lt;/li&gt;
  &lt;li&gt;“你继续说，我就看着不说话”&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;666&quot;&gt;666&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;溜溜溜，指对方做的某件事情很抢眼，很厉害&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;回聊&quot;&gt;回聊&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;回头再聊&lt;/li&gt;
  &lt;li&gt;Talk to you later，回头再聊&lt;/li&gt;
  &lt;li&gt;这个注意可能说出来之后会有结束当前会话的意味，也许没有回头了&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/20624224&quot;&gt;聊天中经常用的「lol」是什么意思？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/35944392&quot;&gt;LMAO 不是老毛，是笑出猪叫 【英文缩写专题课】&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 27 Mar 2019 08:56:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/programmers-english-abbr-in-chat.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/programmers-english-abbr-in-chat.html</guid>
        
        
        <category>miscellaneous</category>
        
      </item>
    
      <item>
        <title>C#/.NET 如何获取一个异常（Exception）的关键特征，用来判断两个异常是否表示同一个异常</title>
        <description>&lt;p&gt;在 .NET / C# 程序中出现异常是很常见的事情，程序出现异常后记录日志或者收集到统一的地方可以便于分析程序中各种各样此前未知的问题。但是，有些异常表示的是同一个异常，只是因为参数不同、状态不同、用户的语言环境不同就分开成多个异常的话，分析起来会有些麻烦。&lt;/p&gt;

&lt;p&gt;本文将提供一个方法，将异常的关键信息提取出来，这样可以比较多次抛出的不同的异常实例是否表示的是同一个异常。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;exceptiontostring&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception.ToString()&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;以下是捕获到的一个异常实例，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 方法后拿到的结果：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapMetadata&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;上可用。&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_Metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在英文的系统上，拿到的结果可能是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapMetadata&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;available&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_Metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们就不能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 来判断两个异常是否表示同一个异常了。&lt;/p&gt;

&lt;p&gt;另外，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 方法中，如果包含 PDB，那么异常堆栈中还会包含源代码文件的路径以及行号信息。&lt;/p&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 中输出的信息，可以阅读 &lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace.ToString()&lt;/code&gt; 方法的源码来了解：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs,693f60dbd83e7853&quot;&gt;StackTrace.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;哪些信息是异常的关键信息&quot;&gt;哪些信息是异常的关键信息&lt;/h2&gt;

&lt;p&gt;从默认的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 中我们可以得知，它包含三个部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;异常类型的全名 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type.FullName&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;异常信息 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception.Message&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;异常堆栈 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception.StackTrace&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;考虑到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 部分受多语言影响非常严重，很难作为关键异常特征，所以我们在提取关键异常特征的时候，需要将这一部分去掉，只能作为此次异常的附加信息，而不能作为关键特征。&lt;/p&gt;

&lt;p&gt;所以我们的关键特征就是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;异常类型的全名 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type.FullName&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;异常堆栈中所有帧的方法签名（这能保证语言无关）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;比如本文一开始列举出来的异常堆栈，我们应该提取成：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_Metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;提取特征的-c-代码&quot;&gt;提取特征的 C# 代码&lt;/h2&gt;

&lt;p&gt;为了提取出以上的关键特征，我需要写一段 C# 代码来做这样的事情：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadonlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stackFrames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StackFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stackFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParameterType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个是拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 实例的类型名称，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;exception.GetType().FullName&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;另一个拿到方法签名。&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception.StackTrace&lt;/code&gt; 属性得到的是一个字符串，而且此字符串还真的有可能根本不是异常信息呢，所以我们这里通过创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace&lt;/code&gt; 的实例来从异常中获取真实的堆栈，当然如果拿不到我们这里使用空数组来表示。&lt;/p&gt;

&lt;p&gt;随后，遍历异常堆栈中的所有帧，将方法名和方法的所有参数进行拼接，形成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClassFullName.MethodName(ParameterType parameterName)&lt;/code&gt; 这样的形式，于是就拼接成类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception.ToString()&lt;/code&gt; 中的格式了。&lt;/p&gt;

&lt;p&gt;由于确定一个类型中是否是同一个方法时与返回值无关，所以我们甚至不需要将返回值加上就能唯一确定一个方法了。&lt;/p&gt;

&lt;h2 id=&quot;一个完整的-exceptiondescriptor&quot;&gt;一个完整的 ExceptionDescriptor&lt;/h2&gt;

&lt;p&gt;为了方便，我写了一个完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDescriptor&lt;/code&gt; 类型来完成异常特征提取的事情。这个类同时重写了相等方法，这样可以直接使用相等方法来判断两个异常的关键信息是否表示的是同一个异常。&lt;/p&gt;

&lt;p&gt;源码可以在这里找到：&lt;a href=&quot;https://gist.github.com/walterlv/0ce95369aa78c5f0f38a527bef5779c2&quot;&gt;https://gist.github.com/walterlv/0ce95369aa78c5f0f38a527bef5779c2&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含一个 &amp;lt;see cref=&quot;Exception&quot;/&amp;gt; 对象的关键特征，可使用此对象的实例判断两个不同的异常实例是否极有可能表示同一个异常。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerDisplay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TypeName,nq}: {FrameSignature[0],nq}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEquatable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取此异常的类型名称。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取此异常堆栈中的所有帧的方法签名，指的是在一个类型中不会冲突的最小部分，所以不含返回值和可访问性。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 比如 private void Foo(Bar b); 方法，在这里会写成 Foo(Bar b)。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 从一个异常中提取出关键的异常特征，并创建 &amp;lt;see cref=&quot;ExceptionDescriptor&quot;/&amp;gt; 的新实例。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;exception&quot;&amp;gt;要提取特征的异常。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExceptionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stackFrames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StackFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stackFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParameterType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 根据异常的信息本身创建异常的关键特征。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;typeName&quot;&amp;gt;异常类型的完整名称。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;frameSignature&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 异常堆栈中的所有帧的方法签名，指的是在一个类型中不会冲突的最小部分，所以不含返回值和可访问性。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 比如 private void Foo(Bar b); 方法，在这里会写成 Foo(Bar b)。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExceptionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 判断此异常特征对象是否与另一个对象实例相等。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 如果参数指定的对象是 &amp;lt;see cref=&quot;ExceptionDescriptor&quot;/&amp;gt;，则判断特征是否相等。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 判断此异常特征与另一个异常特征是否是表示同一个异常。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SequenceEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;unchecked&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;397&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;^&lt;/span&gt;
                       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 判断两个异常特征是否是表示同一个异常。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 判断两个异常特征是否表示的不是同一个异常。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExceptionDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs,693f60dbd83e7853&quot;&gt;StackTrace.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 26 Mar 2019 06:35:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-the-key-descriptor-of-an-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-the-key-descriptor-of-an-exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 CVTE 和广州 .NET 微软技术俱乐部共同举办的 12月8日 广州微软技术沙龙活动</title>
        <description>&lt;p&gt;2018 年 12 月 8 日，在 CVTE·视源股份，在广州黄埔区云埔四路 6 号，我们举办了广州微软技术沙龙。现场参与人数 136 人（不含工作人员），线上参与人员 400+ 人。活动完全免费。&lt;/p&gt;

&lt;p&gt;这是 CVTE·视源股份 和 广州 .NET 微软技术俱乐部 共同举办的一次技术沙龙，我（吕毅）和林德熙，作为微软 MVP，同时是 CVTE 的一员，同时还是广州 .NET 微软技术俱乐部的一员，组织起这次活动，然后在活动中分享了我们的课程 —— 《预编译框架，开发高性能 .NET 应用》。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;虽然本文的创建日期是 12 月 9 号，但实际上内容直到今天才慢慢填完。&lt;/p&gt;

&lt;h2 id=&quot;活动的起源&quot;&gt;活动的起源&lt;/h2&gt;

&lt;p&gt;活动的发起者是“叶伟民”，叶伟民在 2018 年 8 月计划重新复活沉寂了数年的广州 .NET 微软技术俱乐部，&lt;a href=&quot;https://mvp.microsoft.com/en-us/PublicProfile/5003225&quot;&gt;我（吕毅）&lt;/a&gt;和&lt;a href=&quot;https://mvp.microsoft.com/en-us/PublicProfile/5003260&quot;&gt;林德熙&lt;/a&gt;成为了这次复活活动中最早期的一批成员。&lt;/p&gt;

&lt;p&gt;在叶伟民在“广州 .NET 微软技术俱乐部”微信群中表达了希望举办一场活动来正式复活广州 .NET 微软技术俱乐部，我看到消息的第一时间就回应了叶伟民。因为我和林德熙也在计划在广州范围内举办一次线下活动，再加上广州真的也非常需要有这样一个庞大的 .NET 组织来推广微软技术。于是立刻回应我可以在活动中准备一次演讲或者课程，帮助推广微软 .NET 技术。&lt;/p&gt;

&lt;p&gt;叶洪，跟我和林德熙一样，是 &lt;a href=&quot;https://www.zhipin.com/job_detail/26bf2e69fc65103d1HN539S_FFQ~.html&quot;&gt;CVTE·视源股份&lt;/a&gt; 的软件工程师，在收到叶伟民的复活广州 .NET 微软技术俱乐部的提议后，也非常积极响应，准备调动 CVTE·视源股份 的资源，为这次活动提供堪称豪华的场地和服务。&lt;/p&gt;

&lt;p&gt;因为我是做 Windows 应用开发的，和 .NET 技术之间是有很多共通点的，或者说其实也是在大量应用 .NET 技术，所以我只需要稍作调整，即可让我原本在 Windows 应用中适用的技术同样适用于更广泛的 .NET 技术上。&lt;/p&gt;

&lt;h2 id=&quot;活动的计划和准备&quot;&gt;活动的计划和准备&lt;/h2&gt;

&lt;p&gt;在活动之前，叶伟民组织我们十位小伙伴一起在星巴克进行一些活动举办上的讨论，确定下了讲师，活动的组织形式，确定中间可以来一些闪电演讲，确定活动完全免费，而且将活动场地定在了 CVTE·视源股份。&lt;/p&gt;

&lt;p&gt;因为场地选在了 CVTE·视源股份，所以叶洪在这次活动的举办中付出了许许多多的努力，他一直都在加紧与 广州 .NET 微软技术俱乐部 的组织者一起沟通，很多个晚上都在抓紧时间准备活动所需的各种资源和素材，他与公司的各个部分和管理人员取得联系，试图为这次活动争取到足够的场地、时间以及服务。&lt;/p&gt;

&lt;p&gt;而我在准备中向微软中国 MVP 项目组申请了 100 份用于发放给参与者的礼品，为活动制作了几乎全部的视觉设计，和叶洪一起细化活动过程中的各种细节，为活动的各种意外情况准备救场方式。在场地现场调试 21:9 比例的 LED 显示屏，在活动现场部署我们需要的各种环境等等。&lt;/p&gt;

&lt;p&gt;当然，还有跟我在同一个团队的各位小伙伴，也在努力的为活动的每一个细节提供最好的服务。张强、邓蘅、牛彦杰、余冬、丁琛、程遥、李嫚、娄娜……印象中我列举的人数和实际比起来还差非常多……他们为活动现场提供了班车接送、公司参观、公司介绍、茶水准备、零食准备、水果准备、桌椅布置、现场摄影等等各种各样的细节。&lt;/p&gt;

&lt;p&gt;叶伟民为了活动上提高效率，写了一篇给俱乐部成员的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/adalovelacer/p/how-to-ask-a-questiion.html&quot;&gt;广州.NET微软技术俱乐部提技术问题的正确方式 - 叶伟民 - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;我竟然是一位视觉设计师&quot;&gt;我竟然是一位视觉设计师&lt;/h2&gt;

&lt;p&gt;实际上，广州 .NET 微软技术俱乐部在当时还是非常寒碜的，作为一个覆盖整个广州市范围的组织，甚至还没有设计一个 Logo。因为我自己是一位热爱设计的程序员，所以非常乐意在此为 广州 .NET 微软技术俱乐部 设计第一版 Logo。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-13-15-11.png&quot; alt=&quot;广州 .NET 微软技术俱乐部第一版 Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不止如此，我们有三位原生讲师，分别是叶伟民、我（吕毅）和林德熙，但是却连活动专用的 PPT 母版也没有，所以我还为这次活动设计了专门的母版。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-16-31-35.png&quot; alt=&quot;PPT 母版的首页&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-13-24-45.png&quot; alt=&quot;PPT 母版的其他页&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最开始我准备的母版是 16:9 的，但是后来发现场地实际上是 21:9 的大 LED 屏，于是后来把我的整份课件重新制作成了 21:9 的，以便让现场参与的学员能够获得更好的观看体验。&lt;/p&gt;

&lt;p&gt;活动还需要暖场视频、座位表、海报、还有各种各样显示或者打印的图片素材，这些也都缺设计师，所以我也挑起了这些设计任务。&lt;/p&gt;

&lt;p&gt;以下是我设计的报名页，设计得比较素，不过这是我的设计风格。当然，二维码现在已经失效啦！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-27-16-53-18.png&quot; alt=&quot;活动报名设计&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在把这份报名海报发出去的 24 个小时里面，内心真的是非常激动又焦虑，怕人数少了活动组织得尴尬，又怕人数多了我们在现场又安排不过来，造成非常差的体验。&lt;/p&gt;

&lt;h2 id=&quot;活动当天&quot;&gt;活动当天&lt;/h2&gt;

&lt;p&gt;这是活动的重要组织者——叶洪（左），和本次活动主持人，广州 .NET 微软技术俱乐部重要成员——戚亚柱（右）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-11-48.png&quot; alt=&quot;叶洪和戚亚柱&quot; /&gt;&lt;/p&gt;

&lt;p&gt;活动一开始是 Jordan Dong 的演讲 —— 《离你最近的 Windows 10》，这是一个介绍 Windows 10 各项新功能的演讲。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-13-54-21.png&quot; alt=&quot;离你最近的 Windows 10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后是叶伟民的 ABP 框架分享。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-10-57.png&quot; alt=&quot;叶伟民&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看下图！活动当天过来参加的小伙伴们正在认真地聆听我（吕毅）和林德熙的课程 —— 《预编译框架，开发高性能 .NET 应用》。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-13-39-20.png&quot; alt=&quot;活动现场&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现场的小伙伴们也都积极地参与提问和其他各种互动。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-03-14.png&quot; alt=&quot;现场提问&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下图是我和林德熙在解答现场小伙伴的提问。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-02-41.png&quot; alt=&quot;我（吕毅）和林德熙在解答提问&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;活动反馈&quot;&gt;活动反馈&lt;/h2&gt;

&lt;p&gt;在活动结束之后，朋友圈、微信群里到处都有参与的小伙伴对本次活动的评价。&lt;/p&gt;

&lt;p&gt;有表扬 CVTE·视源股份 的，有赞扬 广州 .NET 微软技术俱乐部 的，有表扬课程内容的，也有探讨课程技术细节的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-20-48.png&quot; alt=&quot;活动反馈&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;影响与未来&quot;&gt;影响与未来&lt;/h2&gt;

&lt;p&gt;在这次活动之后，苏州、西安、重庆、北京、上海等各地的 .NET 俱乐部也开始一一复活，全国的 .NET 开发者们又重新聚集到了一起。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-24-14-23-00.png&quot; alt=&quot;活动集体照&quot; /&gt;&lt;/p&gt;

&lt;p&gt;附叶伟民没有写完的总结：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/adalovelacer/p/summary-of-dec-8-2018-event.html&quot;&gt;2018年12月8日广州.NET微软技术俱乐部活动总结 - 叶伟民 - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 Mar 2019 06:25:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/december-event-microsoft-technology-salon.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/december-event-microsoft-technology-salon.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>C#/.NET 如何在第一次机会异常 FirstChanceException 中获取比较完整的异常堆栈</title>
        <description>&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstChangeException&lt;/code&gt; 事件中，我们通常只能拿到异常堆栈的第一帧，这对于我们捕捉到异常是好的，但对分析第一次机会异常可能并不利。&lt;/p&gt;

&lt;p&gt;本文介绍如何在 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstChangeException&lt;/code&gt; 事件中拿到比较完整的异常堆栈，而不只是第一帧。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一次机会异常&quot;&gt;第一次机会异常&lt;/h2&gt;

&lt;p&gt;.NET 程序代码中的任何一段代码，在刚刚抛出异常，还没有被任何处理的那一时刻，&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain&lt;/code&gt; 的实例会引发一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstChanceException&lt;/code&gt; 事件，用于通知此时刚刚开始发生了一个异常。&lt;/p&gt;

&lt;p&gt;这时，这个异常还没有寻找任何一个可以处理它的 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块，在此事件中，你几乎是第一时间拿到了这个异常的信息。&lt;/p&gt;

&lt;p&gt;监听第一次机会异常的代码是这个样子的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FirstChanceException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstChanceExceptionEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里，可以通过 e.Exception 来获取到这个异常。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只不过，在这里我们拿到的异常堆栈只有第一帧，因为这个时候，还没有任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块捕捉到这个异常。比如，我们只能拿到这个：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapMetadata&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;上可用。&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_Metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;一点知识&lt;/strong&gt;：&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 实例的异常堆栈，是从第一次抛出异常的地方开始，到第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 它的地方结束，除非这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块中继续只用 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw;&lt;/code&gt; 抛出才继续向外延伸到下一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;另外，你也可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt; 让内部异常的堆栈也连接起来，详见我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/exceptiondispatchinfo-capture-throw&quot;&gt;使用 ExceptionDispatchInfo 捕捉并重新抛出异常 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;获取较完整的第一次机会异常堆栈&quot;&gt;获取较完整的第一次机会异常堆栈&lt;/h2&gt;

&lt;p&gt;我们需要等到 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstChanceException&lt;/code&gt; 事件中的异常被 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 到，就能获取到第一次抛出的地方到 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 处之间的所有帧。&lt;/p&gt;

&lt;p&gt;所以，我们只需要稍作延迟，即可拿到较完整的异常堆栈：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FirstChanceException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFirstChanceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstChanceExceptionEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 刚刚进入第一次机会异常事件的时候，异常堆栈只有一行，因为此时还没有任何地方 catch。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 现在等待一点点时间，使得异常的堆栈能够延伸到 catch。等待多长不重要，关键是为了让异常得以找到第一个 catch。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 在这里，可以通过 e.Exception 来获取到这个异常。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们可以得到：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapMetadata&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;上可用。&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_Metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Imaging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;err&quot;&gt;在&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，等待多长时间是不重要的，只要不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 就好。因为我们只需要当前调用堆栈中的异常处理执行完成即可。&lt;/p&gt;

&lt;p&gt;关于等待时间，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/sleep-delay-zero-vs-yield&quot;&gt;C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果需要对此异常进行后续的分析，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/get-the-key-descriptor-of-an-exception&quot;&gt;C#/.NET 如何获取一个异常（Exception）的关键特征，用来判断两个异常是否表示同一个异常 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 Mar 2019 04:09:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-get-the-full-stacktrace-of-an-first-chance-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-get-the-full-stacktrace-of-an-first-chance-exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！</title>
        <description>&lt;p&gt;UWP 才能使用的流畅设计效果好惊艳，写新的 UWP 程序可以做出更漂亮的 UI 啦！然而古老的 WPF 项目也想解解馋怎么办？&lt;/p&gt;

&lt;p&gt;于是我动手实现了一个！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;迫不及待看效果&quot;&gt;迫不及待看效果&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-05-16-09-11.gif&quot; alt=&quot;光照效果&quot; /&gt;&lt;br /&gt;
▲ 是不是很像 UWP 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBorderBrush&lt;/code&gt;？&lt;/p&gt;

&lt;p&gt;不止是效果像，连 XAML 写法也像：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50,34,526,348&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border.BorderBrush&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;demo:RevealBorderBrush&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border.BorderBrush&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50,76,526,306&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border.BorderBrush&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;demo:RevealBorderBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FallbackColor=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gray&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border.BorderBrush&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;▲ 模拟得很像的 RevealBorderBrush 的 XAML 写法&lt;/p&gt;

&lt;p&gt;当然，窗口背景那张图是直接用的高斯模糊效果，并不是亚克力 Acrylic 效果。鉴于那张被模糊得看不清的图&lt;strong&gt;是我自己画的&lt;/strong&gt;，所以我一定要单独放出来给大家看🤓！&lt;/p&gt;

&lt;p&gt;我自己画的图，不忍直视，只好模糊掉作为背景了。&lt;a href=&quot;/static/posts/2018-04-05-16-17-15.png&quot;&gt;请点击查看：图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以下是我后来使用此模拟的效果制作的应用。这些应用虽然看起来整个儿都很像 UWP 应用，但都是 100% 纯 WPF；因为我模拟了 UWP 的风格：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-style-using-window-chrome&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/wpf-simulate-native-window-title-bar-buttons&quot;&gt;WPF 应用完全模拟 UWP 的标题栏按钮 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2019 年 1 月更新：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-23-reveal-brush-in-cloud-keyboard-pc.gif&quot; alt=&quot;Cloud Keyboard&quot; /&gt;&lt;br /&gt;
▲ 源码在这个仓库：&lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard&quot;&gt;Walterlv.CloudKeyboard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2019 年 3 月更新：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-23-reveal-in-wpf-window.gif&quot; alt=&quot;Diagnostics Window&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;话不多说看源码&quot;&gt;话不多说看源码&lt;/h2&gt;

&lt;p&gt;UWP 里的 CompositionBrush 是用一个 ShaderEffect 做出所有控件的所有效果的。正如 &lt;a href=&quot;https://www.zhihu.com/people/minmin.gong/activities&quot;&gt;叛逆者&lt;/a&gt; 在 &lt;a href=&quot;https://www.zhihu.com/question/59724483/answer/168191216?utm_medium=social&amp;amp;utm_source=wechat_session&quot;&gt;如何评价微软在 Build 2017 上提出的 Fluent Design System？ - 知乎&lt;/a&gt; 一文中说的，只需要极少的计算量就能完成。&lt;/p&gt;

&lt;p&gt;不过 Win32 窗口并没有得到眷恋，所以我只好自己实现。但限于只能使用 WPF 内建机制，故性能上当然不能比了。但在小型项目的局部用用还是非常不错的——尤其是个人项目！&lt;em&gt;不过话说现在个人项目谁还用 WPF 呢&lt;/em&gt; (逃&lt;/p&gt;

&lt;p&gt;思路是画一个径向渐变，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;RadialGradientBrush&lt;/code&gt;，然后当鼠标在窗口内移动时，改变径向渐变的渐变中心为鼠标所在点。&lt;/p&gt;

&lt;p&gt;以下是全部源码。&lt;strong&gt;不要在意基类啦！WPF 不让我们实现自己的 Brush，所以只好用 MarkupExtension 绕道实现了。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2019 年 3 月更新：&lt;/strong&gt;以下源码中现在使用了全局光照，也就是说，就算你的控件不在一个固定的窗口中，也会使用到光照效果了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ReSharper disable CheckNamespace&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Effects&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Paints a control border with a reveal effect using composition brush and light effects.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RevealBorderBrushExtension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupExtension&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadStatic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// The color to use for rendering in case the &amp;lt;see cref=&quot;MarkupExtension&quot;/&amp;gt; can't work correctly.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FallbackColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Gets or sets a value that specifies the base background color for the brush.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RelativeTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Opacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Radius&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProvideValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果没有服务，则直接返回。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IProvideValueTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IProvideValueTarget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// MarkupExtension 在样式模板中，返回 this 以延迟提供值。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharedDp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetObject&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesignerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetIsInDesignMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SolidColorBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FallbackColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateGlobalBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;rootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MouseMove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnMouseMove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnMouseMove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MouseEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;UpdateBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateGlobalBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rendering&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rendering&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnRendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRendering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toCollect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;Reveal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;toCollect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toCollect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_globalRevealingElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Reveal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;UpdateBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsUsingMouseOrStylus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GradientOrigin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NegativeInfinity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NegativeInfinity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RadialGradientBrush&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RadialGradientBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;MappingMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BrushMappingMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RadiusX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RadiusY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Opacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RelativeTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RelativeTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NegativeInfinity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NegativeInfinity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsUsingMouseOrStylus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stylus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentStylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stylus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/design/style/reveal?wt.mc_id=MVP&quot;&gt;突出显示 - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 Mar 2019 03:09:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fluent-design-reveal-brush-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fluent-design-reveal-brush-in-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>xaml</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用 Microsoft.Net.Compilers 在旧版本的 Visual Studio 2013/2015/2017 中开启新的 C# 7.x 和 C# 8 语法</title>
        <description>&lt;p&gt;新版本的 C# 特性需要新版本的 Visual Studio 的支持。不过，如果你不介意修改项目的话，你也能在低版本的 Visual Studio 中获得高版本的 C# 语言支持了。&lt;/p&gt;

&lt;p&gt;而使用 Microsoft.Net.Compilers 这款 NuGet 包就可以做到。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;不同-visual-studio-原生支持的-c-版本&quot;&gt;不同 Visual Studio 原生支持的 C# 版本&lt;/h2&gt;

&lt;p&gt;Visual Studio 每一次的重大发布都带来新的 C# 版本（至少在 Visual Studio 2017 之前是这样），于是通常情况下如果你使用了旧版本的 Visual Studio，还打不开编写了新 C# 语法的项目呢！&lt;/p&gt;

&lt;p&gt;你可以阅读另一篇文章了解不同 Visual Studio 版本原生带来的 C# 版本。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/csharp-version-histories&quot;&gt;各个 C# 版本的主要特性、发布日期和发布方式（C# 1.0 - 7.3） - 吕毅&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;引入-microsoftnetcompilers&quot;&gt;引入 Microsoft.Net.Compilers&lt;/h2&gt;

&lt;p&gt;不过，伴随着 .NET Core 生态的崛起和 NuGet 的逐渐广泛的使用，微软发布了 &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Net.Compilers/&quot;&gt;Microsoft.Net.Compilers&lt;/a&gt; 来解决跨 Visual Studio 版本的 C# 语言版本兼容问题了。&lt;/p&gt;

&lt;p&gt;Microsoft.Net.Compilers 首次发布于 2015 年 7 月。&lt;/p&gt;

&lt;p&gt;官方对齐的描述是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;.NET Compilers package.&lt;br /&gt;
Referencing this package will cause the project to be built using the specific version of the C# and Visual Basic compilers contained in the package, as opposed to any system installed version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是一个 .NET 的编译器包，无论你系统中安装的是什么版本的 C# 编译器，使用此包都可以强制项目使用某个特定版本的 C# 编译器。&lt;/p&gt;

&lt;h2 id=&quot;使用-microsoftnetcompilers&quot;&gt;使用 Microsoft.Net.Compilers&lt;/h2&gt;

&lt;h3 id=&quot;第一步安装-net-framework-46-或以上&quot;&gt;第一步：安装 .NET Framework 4.6 或以上&lt;/h3&gt;

&lt;p&gt;Microsoft.Net.Compilers 对项目本身没有什么要求，但需要编译项目的计算机上安装有完整功能的 .NET Framework 4.6 及以上版本。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This package can be used to compile code targeting any platform, but can only be run using the desktop .NET 4.6+ Full Framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是开发者计算机上的事情，不影响产品的 .NET Framework 版本需求。如果你连系统也比较旧，那么安装下最新版本的 .NET Framework 即可。&lt;/p&gt;

&lt;h3 id=&quot;第二步安装-nuget-包-microsoftnetcompilers&quot;&gt;第二步：安装 NuGet 包 Microsoft.Net.Compilers&lt;/h3&gt;

&lt;p&gt;在你需要编写最新版本 C# 的项目中安装 NuGet 包 Microsoft.Net.Compilers。&lt;/p&gt;

&lt;h3 id=&quot;第三步编辑项目使用最新版本的-c-语言&quot;&gt;第三步：编辑项目使用最新版本的 C# 语言&lt;/h3&gt;

&lt;p&gt;就像普通的项目启用最新版 C# 语言一样，在你的项目的 csproj 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 中添加以下属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;Latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果不知道如何添加，可以阅读 &lt;a href=&quot;https://blog.lindexi.com/post/VisualStudio-%E4%BD%BF%E7%94%A8%E4%B8%89%E4%B8%AA%E6%96%B9%E6%B3%95%E5%90%AF%E5%8A%A8%E6%9C%80%E6%96%B0-C-%E5%8A%9F%E8%83%BD.html&quot;&gt;VisualStudio 使用三个方法启动最新 C# 功能 - 林德熙&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;开始使用最新版本的-c-特性&quot;&gt;开始使用最新版本的 C# 特性&lt;/h3&gt;

&lt;p&gt;你已经可以使用最新版本的 C# 了，而不用关心你本机安装的是哪个版本 —— 即便你是 Visual Studio 2013/2015。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Thanks Walterlv!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I got the latest C# version.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://programmium.wordpress.com/2017/11/13/c-enabling-c-7-1-on-visual-studio-2015-2013/&quot;&gt;C# : Enabling C# 7.1 on Visual Studio 2015 / 2013 – programmium&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Net.Compilers/&quot;&gt;NuGet Gallery - Microsoft.Net.Compilers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/34548597/6233938&quot;&gt;c# - What is the Purpose of Microsoft.Net.Compilers? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/opserver/Opserver/issues/271&quot;&gt;Remove dependency of Microsoft.Net.Compilers NuGet Package · Issue #271 · opserver/Opserver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 23 Mar 2019 02:30:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-lastest-csharp-support-for-old-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-lastest-csharp-support-for-old-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>制作一个极简的 .NET 客户端应用自安装或自更新程序</title>
        <description>&lt;p&gt;本文主要说的是 .NET 客户端应用，可以是只能在 Windows 端运行的基于 .NET Framework 或基于 .NET Core 的 WPF / Windows Forms 应用，也可以是其他基于 .NET Core 的跨平台应用。但是不是那些更新权限受到严格控制的 UWP / iOS / Android 应用。&lt;/p&gt;

&lt;p&gt;本文将编写一个简单的程序，这个程序初次运行的时候会安装自己，如果已安装旧版本会更新自己，如果已安装最新则直接运行。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;自安装或自更新的思路&quot;&gt;自安装或自更新的思路&lt;/h2&gt;

&lt;p&gt;简单的安装过程实际上是 &lt;code class=&quot;highlighter-rouge&quot;&gt;解压 + 复制 + 配置 + 外部命令&lt;/code&gt;。这里，我只做 &lt;code class=&quot;highlighter-rouge&quot;&gt;复制 + 配置 + 外部命令&lt;/code&gt;，并且把 &lt;code class=&quot;highlighter-rouge&quot;&gt;配置 + 外部命令&lt;/code&gt; 合为一个步骤。&lt;/p&gt;

&lt;p&gt;于是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;启动后，检查安装路径下是否有已经安装的程序；&lt;/li&gt;
  &lt;li&gt;如果没有，则直接复制自己过去；&lt;/li&gt;
  &lt;li&gt;如果有，则比较版本号，更新则复制过去。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;本文用到的知识&quot;&gt;本文用到的知识&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-with-lowered-uac-privileges&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/rename-executable-self-when-running&quot;&gt;Windows 上的应用程序在运行期间可以给自己改名（可以做 OTA 自我更新） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/get-attributes-for-reflection-only-loaded-assembly&quot;&gt;仅反射加载（ReflectionOnlyLoadFrom）的 .NET 程序集，如何反射获取它的 Attribute 元数据呢？ - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;

&lt;p&gt;于是我写了一个简单的类型用来做自安装。创建完 &lt;code class=&quot;highlighter-rouge&quot;&gt;SelfInstaller&lt;/code&gt; 的实例后，根据安装完的结果做不同的行为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;显示安装成功的窗口&lt;/li&gt;
  &lt;li&gt;显示正常的窗口&lt;/li&gt;
  &lt;li&gt;关闭自己&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Installing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.ENPlugins.Presentation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SelfInstaller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\lvyi\AppData\Local\Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryInstall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InstallTipWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Same&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ran&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShouldRerun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;Shutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;附全部源码&quot;&gt;附全部源码&lt;/h2&gt;

&lt;p&gt;本文代码在 &lt;a href=&quot;https://gist.github.com/walterlv/33bdd62e2411c69c2699038e2bc97488&quot;&gt;https://gist.github.com/walterlv/33bdd62e2411c69c2699038e2bc97488&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.EasiPlugins.Installing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 自安装或字更新的安装器。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SelfInstaller&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 初始化 &amp;lt;see cref=&quot;SelfInstaller&quot;/&amp;gt; 的新实例。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;targetFilePath&quot;&amp;gt;要安装的主程序的目标路径。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;installingProcedure&quot;&amp;gt;如果需要在安装后执行额外的安装步骤，则指定自定义的安装步骤。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SelfInstaller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInstallingProcedure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installingProcedure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCallingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssemblyTitleAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;targetFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;extensionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;InstallingProcedure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installingProcedure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取要安装的主程序的目标路径。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取或设置当应用重新启动自己的时候应该使用的参数。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RunSelfArguments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;--rerun-reason {reason}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取此自安装器安装中需要执行的自定义安装步骤。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInstallingProcedure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstallingProcedure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 尝试安装，并返回安装结果。调用者可能需要对安装结果进行必要的操作。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInstall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InstallOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 已安装或更新，由已安装的程序处理安装后操作。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Same&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShouldRerun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildRerunArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstallingProcedure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AfterInstall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ran&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 进行安装或更新。执行后将返回安装状态以及安装后的目标程序路径。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InstallOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selfFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 判断当前是否已经运行在插件目录下。如果已经在那里运行，那么不需要安装。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selfFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 继续运行自己即可。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ran&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 判断插件目录下的软件版本是否比较新，如果插件目录已经比较新，那么不需要安装。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isOldOneExists&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isOldOneExists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNewer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckIfNewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 运行已安装目录下的自己。&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Same&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 将自己复制到插件目录进行安装。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;succeedOnce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CopySelfToInstall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;succeedOnce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 如果不是一次就成功，说明目标被占用。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isOldOneExists&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckIfNewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installedVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReflectionOnlyLoadFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installedVersionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssemblyFileVersionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructorArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;installedVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;installedVersionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileLoadException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;installedVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BadImageFormatException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;installedVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentVersionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssemblyFileVersionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentVersionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installedVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 将自己复制到目标安装路径。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CopySelfToInstall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFolder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetFileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selfFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isInUse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.bak&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selfFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 不退出循环，于是会重试。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;isInUse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 生成用于重启自身的启动参数。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;rerunReason&quot;&amp;gt;表示重启原因的一个单词（不能包含空格）。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;includeExecutablePath&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;executablePath&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildRerunArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;includeExecutablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;重启原因不能包含空格&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;includeExecutablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSelfArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSelfArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{reason}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rerunReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 表示安装完后的状态。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstalledState&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 已安装。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 已更新。说明运行此程序时，已经存在一个旧版本的应用。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 已更新。但是原始文件被占用，可能需要重启才可使用。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;UpdatedInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 已代理启动新的程序，所以此程序需要退出。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ShouldRerun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 两个程序都是一样的，跑谁都一样。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Same&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 没有执行安装、更新或代理，表示此程序现在是正常启动。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Ran&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 21 Mar 2019 16:44:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/simple-windows-app-self-installer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/simple-windows-app-self-installer.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 同一窗口内的多线程/多进程 UI（使用 SetParent 嵌入另一个窗口）</title>
        <description>&lt;p&gt;WPF 的 UI 逻辑只在同一个线程中，这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI，大家也会想到使用另一个窗口来实现，让每个窗口拥有自己的 UI 线程。然而，就不能让同一个窗口内部使用多个 UI 线程吗？&lt;/p&gt;

&lt;p&gt;阅读本文将收获一份 Win32 函数 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 及相关函数的使用方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;WPF 同一个窗口中跨线程访问 UI 有多种方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/multi-thread-ui-using-visualtarget-in-wpf&quot;&gt;使用 VisualTarget (本文)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/embed-win32-window-using-csharp&quot;&gt;使用 SetParent 嵌入另一个窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前者使用的是 WPF 原生方式，做出来的跨线程 UI 可以和原来的 UI 相互重叠遮挡。后者使用的是 Win32 的方式，实际效果非常类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowsFormsHost&lt;/code&gt;，新线程中的 UI 在原来的所有 WPF 控件上面遮挡。另外，后者不止可以是跨线程，还可以跨进程。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备必要的-win32-函数&quot;&gt;准备必要的 Win32 函数&lt;/h2&gt;

&lt;p&gt;完成基本功能所需的 Win32 函数是非常少的，只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MoveWindow&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWndNewParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bRepaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 用于指定传统的窗口父子关系。有多传统呢？呃……就是 Windows 自诞生以来的那种传统。在传统的 Win32 应用程序中，每一个控件都有自己的窗口句柄，它们之间通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 进行连接；可以说一个 Button 就是一个窗口。而我们现在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetParent&lt;/code&gt; 其实就是在使用传统 Win32 程序中的控件的机制。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveWindow&lt;/code&gt; 用于指定窗口相对于其父级的位置，我们使用这个函数来决定新嵌入的窗口在原来界面中的位置。&lt;/p&gt;

&lt;h2 id=&quot;启动后台-ui-线程&quot;&gt;启动后台 UI 线程&lt;/h2&gt;

&lt;p&gt;启动一个后台的 WPF UI 线程网上有不少线程的方法，但大体思路是一样的。我之前在 &lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;如何实现一个可以用 await 异步等待的 Awaiter&lt;/a&gt; 一文中写了一个利用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 做的更高级的版本。&lt;/p&gt;

&lt;p&gt;为了继续本文，我将上文中的核心文件抽出来做成了 GitHubGist，访问 &lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb&quot;&gt;Custom awaiter with background UI thread&lt;/a&gt; 下载那三个文件并放入到自己的项目中。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AwaiterInterfaces.cs&lt;/code&gt; &lt;em&gt;为实现 async/await 机制准备的一些接口，虽然事实上可以不需要，不过加上可以防逗比。&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation.cs&lt;/code&gt; &lt;em&gt;这是我自己实现的自定义 awaiter，可以利用 awaiter 的回调函数机制规避线程同步锁的使用。&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher.cs&lt;/code&gt; &lt;em&gt;用于创建后台 UI 线程的类型，这个文件包含本文需要使用的核心类，使用到了上面两个文件。&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在使用了上面的三个文件的情况下，创建一个后台 UI 线程并获得用于执行代码的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 只需要一句话：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 传入的参数是线程的名称，也可以不用传。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunNewAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background UI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在得到了后台 UI 线程 Dispatcher 的情况下，无论做什么后台线程的 UI 操作，只需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dispatcher.InvokeAsync&lt;/code&gt; 即可。&lt;/p&gt;

&lt;p&gt;我们使用下面的句子创建一个后台线程的窗口并显示出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backgroundWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在代码中，我们监听了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceInitialized&lt;/code&gt; 事件。这是 WPF 窗口刚刚获得 Windows 窗口句柄的时机，在此事件中，我们可以最早地拿到窗口句柄以便进行 Win32 函数调用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里可以获取到窗口句柄。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;嵌入窗口&quot;&gt;嵌入窗口&lt;/h2&gt;

&lt;p&gt;为了比较容易写出嵌入窗口的代码，我将核心部分代码贴出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParentWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ParentWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 获取父窗口的窗口句柄。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_parentHwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 在后台线程创建子窗口。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunNewAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background UI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;SetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_parentHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MoveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_parentHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWndNewParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bRepaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体执行嵌入窗口的是这一段：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_parentHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;MoveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;childHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终显示时会将后台线程的子窗口显示到父窗口的 (0, 0, 300, 300) 的位置和大小。可以试试在主线程写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Sleep(5000)&lt;/code&gt;，在卡顿的事件内，你依然可以拖动子窗口的标题栏进行拖拽。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-11-21-31-07.png&quot; alt=&quot;嵌入了后台线程的窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，如果你认为外面那一圈窗口的非客户区太丑了，使用普通设置窗口属性的方法去掉即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BorderBrush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DodgerBlue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BorderThickness&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Teal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResizeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoResize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextBlock&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;HorizontalAlignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HorizontalAlignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;VerticalAlignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VerticalAlignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Foreground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FontSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-11-21-33-55.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;源码&quot;&gt;源码&lt;/h2&gt;

&lt;p&gt;以上代码中使用到了我之前的一些源码，这几个文件可分别从以下链接找到并下载到你的项目中：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Annotations/Annotations.cs&quot;&gt;Annotations.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-awaiterinterfaces-cs&quot;&gt;AwaiterInterfaces.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-dispatcherasyncoperation-cs&quot;&gt;DispatcherAsyncOperation.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-uidispatcher-cs&quot;&gt;UIDispatcher.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/VisualTargetPresentationSource.cs&quot;&gt;VisualTargetPresentationSource.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Thu, 21 Mar 2019 02:13:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/embed-win32-window-using-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/embed-win32-window-using-csharp.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果</title>
        <description>&lt;p&gt;如果你的程序对 Windows 运行权限有要求，那么需要设置应用程序清单。本文介绍如何添加应用程序清单，并解释其中各项 UAC 权限设置的实际效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;阅读本文之前，你可能需要了解如何创建应用程序清单文件。阅读我的另一篇博客可以了解：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-manifest-file-for-application&quot;&gt;如何创建应用程序清单文件 App.Manifest，如何创建不带清单的应用程序 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;各种不同的-uac-清单选项&quot;&gt;各种不同的 UAC 清单选项&lt;/h2&gt;

&lt;p&gt;从默认生成的应用程序清单中，我们可以很容易的知道有四种不同的设置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;asInvoker&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;requireAdministrator&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;highestAvailable&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;requestedExecutionLevel&lt;/code&gt; 元素 &lt;em&gt;（不要忘了还可以删除）&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然这里我们是没有考虑 &lt;code class=&quot;highlighter-rouge&quot;&gt;uiAccess&lt;/code&gt; 的。你可以阅读我的另一篇博客了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;uiAccess&lt;/code&gt; 的一项应用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/wpf/2015/03/31/run-desktop-application-above-windows-application.html&quot;&gt;让 Windows 桌面程序运行在 Windows 应用上面 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;asinvoker&quot;&gt;asInvoker&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;父进程是什么权限级别，那么此应用程序作为子进程运行时就是什么权限级别。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;默认情况下用户启动应用程序都是使用 Windows 资源管理器（explorer.exe）运行的；在开启了 UAC 的情况下，资源管理器是以标准用户权限运行的。于是对于用户点击打开的应用程序，默认就是以标准用户权限运行的。&lt;/p&gt;

&lt;p&gt;如果已经以管理员权限启动了一个程序，那么这个程序启动的子进程也会是管理员权限。典型的情况是一个应用程序安装包安装的时候使用管理员权限运行，于是这个安装程序在安装完成后启动的这个应用程序进程实例就是管理员权限的。有时候这种设定会出现问题，你可以阅读 &lt;a href=&quot;/post/start-process-with-lowered-uac-privileges&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;requireadministrator&quot;&gt;requireAdministrator&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;此程序需要以管理员权限运行。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在资源管理器中可以看到这样的程序图标的右下角会有一个盾牌图标。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-16-34-34.png&quot; alt=&quot;管理员权限图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用户在资源管理器中双击启动此程序，或者在程序中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 启动此程序，会弹出 UAC 提示框。点击“是”会提权，点击“否”则操作取消。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-16-42-45.png&quot; alt=&quot;UAC 弹窗&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;highestavailable&quot;&gt;highestAvailable&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;此程序将以当前用户能获取的最高权限来运行。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这个概念可能会跟前面说的 &lt;code class=&quot;highlighter-rouge&quot;&gt;requireAdministrator&lt;/code&gt; 弄混淆。&lt;/p&gt;

&lt;p&gt;要更好的理解这两个概念的区别，你可能需要对 UAC 用户账户控制有一个初步的了解，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接下来的内容，都假设你已经了解了上文所述的 UAC 用户账户控制。&lt;/p&gt;

&lt;p&gt;如果你指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;highestAvailable&lt;/code&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当你在管理员账户下运行此程序，就会要求权限提升。资源管理器上会出现盾牌图标，双击或使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 启动此程序会弹出 UAC 提示框。在用户同意后，你的程序将获得完全访问令牌（Full Access Token）。&lt;/li&gt;
  &lt;li&gt;当你在标准账户下运行此程序，此账户的最高权限就是标准账户。受限访问令牌（Limited Access Token）就是当前账户下的最高令牌了，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;highestAvailable&lt;/code&gt; 已经达到了要求。资源管理器上不会出现盾牌图标，双击或使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 启动此程序也不会出现 UAC 提示框，此程序将以受限权限执行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下图是一个例子。lvyi 是我安装系统时创建的管理员账号，但是我使用的是 walterlv 标准账号。正常是在 walterlv 账号下启动程序，但以管理员权限运行时，会要求输入 lvyi 账号的密码来提权，于是就会以 lvyi 的身份运行这个程序。这种情况下，那个管理员权限运行的程序会以为当前运行在 lvyi 这个账户下，程序员需要小心这里的坑，因为拿到的用户路径以及注册表不是你所期望的 walterlv 这个账号下的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-16-57-48.png&quot; alt=&quot;标准账户下运行管理员权限程序会切换账户&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;删除-requestedexecutionlevel-元素&quot;&gt;删除 requestedExecutionLevel 元素&lt;/h2&gt;

&lt;p&gt;删除 requestedExecutionLevel 元素指的是将下面标注的这一行删掉：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
    &amp;lt;assembly manifestVersion=&quot;1.0&quot; xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&amp;gt;
      &amp;lt;trustInfo xmlns=&quot;urn:schemas-microsoft-com:asm.v2&quot;&amp;gt;
        &amp;lt;security&amp;gt;
          &amp;lt;requestedPrivileges xmlns=&quot;urn:schemas-microsoft-com:asm.v3&quot;&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--          &amp;lt;requestedExecutionLevel level=&quot;asInvoker&quot; uiAccess=&quot;false&quot; /&amp;gt;
&lt;/span&gt;          &amp;lt;/requestedPrivileges&amp;gt;
        &amp;lt;/security&amp;gt;
      &amp;lt;/trustInfo&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注释中说删除 &lt;code class=&quot;highlighter-rouge&quot;&gt;requestedExecutionLevel&lt;/code&gt; 元素将开启 UAC 虚拟化。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-17-15-24.png&quot; alt=&quot;开启 UAC 虚拟化&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我将这个节点删除后，运行我的 Demo 程序后 UAC 虚拟化将启用。默认这里是“已禁用”的。&lt;/p&gt;

&lt;p&gt;不过在以下任意一种情况下，UAC 虚拟化即便删了 &lt;code class=&quot;highlighter-rouge&quot;&gt;requestedExecutionLevel&lt;/code&gt; 也是不会开启的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;64 位进程&lt;/li&gt;
  &lt;li&gt;不可交互的进程（例如服务）&lt;/li&gt;
  &lt;li&gt;进程模拟用户的操作（如果一个进程像用户一样执行了某项操作，那么这个操作不会被虚拟化）&lt;/li&gt;
  &lt;li&gt;驱动等内核模式进程&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这部分的列表你可以在这里查询到：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/sysinfo/registry-virtualization#registry-virtualization-scope&quot;&gt;Registry Virtualization - Windows applications - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;这些值都用于什么场景&quot;&gt;这些值都用于什么场景？&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;asInvoker&lt;/code&gt; 是默认情况下的首选。如果你的程序没有什么特殊的需求，就使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;asInvoker&lt;/code&gt;；就算你的程序需要管理员程序做一些特殊的任务，那最好也写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;asInvoker&lt;/code&gt;，仅在必要的时候才进行管理员权限提升。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;requireAdministrator&lt;/code&gt;，只有当你的程序大量进行需要管理员权限的操作的时候才建议使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;requireAdministrator&lt;/code&gt; 值，例如你正在做安装程序。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;highestAvailable&lt;/code&gt;，当你的程序需要管理员权限，但又要求仅对当前用户修改时设置为此值。因为标准用户申请 UAC 提权之后会以其他用户的身份运行进程，这就不是对当前用户的操作了；使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;highestAvailable&lt;/code&gt; 来确保以当前用户运行。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;为什么-uwp-程序不能指定-uac-清单选项&quot;&gt;为什么 UWP 程序不能指定 UAC 清单选项？&lt;/h2&gt;

&lt;p&gt;在我的另一篇博客 &lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制&lt;/a&gt; 中说到了访问令牌。&lt;/p&gt;

&lt;p&gt;UWP 程序只能获得受限访问令牌，没得选，所以也就不需要指定 UAC 清单选项了。这也是为什么当你关闭 UAC 之后，UWP 程序将全部闪退的重要原因。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/sysinfo/registry-virtualization#registry-virtualization-scope&quot;&gt;Registry Virtualization - Windows applications - Microsoft Docs&lt;/a&gt;
&lt;!-- - [UAC 实现原理及绕过方法 - _chesky - 博客园](https://www.cnblogs.com/Chesky/p/UAC_Bypass.html) --&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/security/how-user-account-control-uac-affects-your-application&quot;&gt;How User Account Control (UAC) Affects Your Application - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 20 Mar 2019 13:39:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/requested-execution-level-of-application-manifest.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/requested-execution-level-of-application-manifest.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Windows 系统上使用任务管理器查看进程的各项属性（命令行、DPI、管理员权限等）</title>
        <description>&lt;p&gt;Windows 系统上的任务管理器进化到 Windows 10 的 1809 版本后，又新增了几项可以查看的进程属性。&lt;/p&gt;

&lt;p&gt;本文介绍可以使用任务管理器查看的各种进程属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何查看进程的各种属性&quot;&gt;如何查看进程的各种属性&lt;/h2&gt;

&lt;p&gt;在任务栏上右键，选择“任务管理器”；或者按下 Ctrl + Shift + Esc 可以打开任务管理器。如果你的电脑死掉了，也可以按 Ctrl + Alt + Del 再选择任务管理器打开。&lt;/p&gt;

&lt;p&gt;在顶部列表标题上右键，可以选择列，在这里可以打开和关闭各种各样可以查看的进程属性。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-18-51-09.png&quot; alt=&quot;任务管理器，选择列&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;名称pid状态&quot;&gt;名称、PID、状态&lt;/h2&gt;

&lt;p&gt;名称不用多说，就是启动这个进程时的程序文件的名称。&lt;/p&gt;

&lt;p&gt;值得注意的是，名称自进程启动时就确定了，即便你在运行期间改了名字，进程名也不会变。关于运行期间改名，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/rename-executable-self-when-running&quot;&gt;Windows 上的应用程序在运行期间可以给自己改名（可以做 OTA 自我更新） - 吕毅&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PID 可以唯一确定当前系统运行期间的一个进程，所以用 PID 来找到进程是最靠谱的（前提是你拿得到）。这里有一个有意思的事情，可以阅读这些文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.oschina.net/question/23734_29378&quot;&gt;Windows 的 PID为什么是 4 的倍数 - 开源中国社区&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/Thriving-Country/archive/2011/09/18/2180143.html&quot;&gt;WINDOWS 进程或线程号为什么是 4 的倍数 - GUO Xingwang - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;进程的状态可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/freeelinux/article/details/53562592&quot;&gt;进程的挂起状态详细分析 - FreeeLinux’s blog - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;路径名称命令行&quot;&gt;路径名称、命令行&lt;/h2&gt;

&lt;p&gt;路径名称可以帮助我们了解这个进程是由计算机上的哪个程序启动产生的。&lt;/p&gt;

&lt;p&gt;不过我更喜欢的是“命令行”。因为除了可以看进程的路径之外，还可以了解到它是如何启动的。比如下面这篇博客中，我就是在任务管理器了解到这些工具的启动参数的。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/customize-external-tools-for-visual-studio&quot;&gt;使用 Visual Studio 自定义外部命令 (External Tools) 快速打开 git bash 等各种工具 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于命令行中的路径，可以参见我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/when-will-the-command-line-args-contain-the-executable-path&quot;&gt;.NET 命令行参数包含应用程序路径吗？ - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/get-command-line-for-a-running-process&quot;&gt;.NET/C# 获取一个正在运行的进程的命令行参数 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;用户名特权uac-虚拟化&quot;&gt;用户名、特权、UAC 虚拟化&lt;/h2&gt;

&lt;p&gt;我把这三项放在一起说，是因为这三项是与 UAC 相关的项。&lt;/p&gt;

&lt;p&gt;用户名指的是启动此进程的那个用户的用户名，这在调试一些提权程序的时候可能会有用。因为对于管理员账户而言，提权前后是同一个用户；而对于标准账户，提权后进程将是管理员账户的进程，于是两个进程运行在不同的用户空间下，可能协作上会出现一些问题。&lt;/p&gt;

&lt;p&gt;关于用户账户以及提权相关的问题，可以阅读 &lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;特权（Privilege）指的是此进程是否运行在管理员权限下。值为“是”则运行在管理员权限下，值为“否”则运行在标准账户权限下。&lt;/p&gt;

&lt;p&gt;关于特权级别相关的问题，可以阅读 &lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;UAC 虚拟化相关的问题可以阅读 &lt;a href=&quot;/post/requested-execution-level-of-application-manifest&quot;&gt;应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;dpi-感知&quot;&gt;DPI 感知&lt;/h2&gt;

&lt;p&gt;可以查看进程的 DPI 感知级别。&lt;/p&gt;

&lt;p&gt;进程的 DPI 感知级别有以下这些，名字来源于 Windows 系统任务管理器上的显示名称。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不知道 (Unaware)&lt;/li&gt;
  &lt;li&gt;系统 (System DPI Awareness)&lt;/li&gt;
  &lt;li&gt;每个显示器 (Per-Monitor DPI Awareness)&lt;/li&gt;
  &lt;li&gt;每个显示器(v2) (Per-Monitor V2 DPI Awareness)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 DPI 感知级别的更多内容，可以阅读我的其他博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development-for-wpf&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 11:52:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/view-process-info-using-task-manager.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/view-process-info-using-task-manager.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何创建一个基于 .NET Core 3 的 WPF 项目</title>
        <description>&lt;p&gt;在 Connect(); 2018 大会上，微软发布了 .NET Core 3 Preview，以及基于 .NET Core 3 的 WPF；同时还发布了 Visual Studio 2019 预览版。不过 Visual Studio 2019 的预览版中并没有携带 WPF on .NET Core 3 的模板，于是新建项目的时候并不能快速创建一个基于 .NET Core 3 的 WPF 项目。&lt;/p&gt;

&lt;p&gt;本文将指导大家如何创建一个基于 .NET Core 3 的 WPF 项目。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-net-core-30-preview-sdk&quot;&gt;安装 .NET Core 3.0 Preview SDK&lt;/h2&gt;

&lt;p&gt;前往官网下载：&lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-core/3.0&quot;&gt;.NET Core 3.0 downloads for Linux, macOS, and Windows&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;然后安装。&lt;/p&gt;

&lt;p&gt;如果你没有安装 Visual Studio 2019 Preview，请前往下载：&lt;a href=&quot;https://visualstudio.microsoft.com/vs/preview/&quot;&gt;Visual Studio 2019&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用-visual-studio-2019-创建&quot;&gt;使用 Visual Studio 2019 创建&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;启动 Visual Studio 2019，选择“创建新项目”&lt;/li&gt;
  &lt;li&gt;选择 WPF App (.NET Core)，下一步&lt;/li&gt;
  &lt;li&gt;输入项目名称、位置和解决方案名称，创建&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-18-26-22.png&quot; alt=&quot;创建新项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-18-26-27.png&quot; alt=&quot;WPF App (.NET Core)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-18-26-33.png&quot; alt=&quot;创建&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用命令行创建&quot;&gt;使用命令行创建&lt;/h2&gt;

&lt;p&gt;刚刚发布 .NET Core 3.0 和 Visual Studio 2019 第一个预览版的时候，Visual Studio 还不能创建 .NET Core 3.0 的 WPF 程序，所以会有这一小节用命令行来创建。&lt;/p&gt;

&lt;p&gt;当然，有时我也会用 Visual Studio Code 来写简单的程序，这个时候也用得到命令行：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/equip-vscode-for-dotnet-core-app-debugging&quot;&gt;让你的 VSCode 具备调试 C# 语言 .NET Core 程序的能力 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;运行新建命令&quot;&gt;运行新建命令&lt;/h3&gt;

&lt;p&gt;在桌面或其他你要新建项目的文件夹中打开 PowerShell，然后输入命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wpf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;WalterlvWpfApp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvWPfApp&lt;/code&gt; 是 WPF 项目的名称。&lt;/p&gt;

&lt;p&gt;这时，你会在你刚刚准备的文件夹中发现刚刚新建的 WPF 项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-06-08-52-20.png&quot; alt=&quot;刚刚新建的 WPF 项目&quot; /&gt;&lt;br /&gt;
▲ 刚刚新建的 WPF 项目&lt;/p&gt;

&lt;h3 id=&quot;打开这个-csproj-文件&quot;&gt;打开这个 csproj 文件&lt;/h3&gt;

&lt;p&gt;在 Visual Studio 中打开这个 csproj 文件即可在 Visual Studio 2019 Preview 中基于这个新的 WPF on .NET Core 3 的项目进行开发。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-06-08-55-09.png&quot; alt=&quot;新的 WPF 项目&quot; /&gt;&lt;br /&gt;
▲ 新的 WPF 项目。&lt;/p&gt;

&lt;h2 id=&quot;更多&quot;&gt;更多&lt;/h2&gt;

&lt;p&gt;如果你希望将现有基于 .NET Framework 的 WPF 项目迁移到 .NET Core 3，那么请阅读我的另一篇博客：&lt;a href=&quot;/post/migrate-wpf-project-from-dotnet-framework-to-dotnet-core&quot;&gt;将基于 .NET Framework 的 WPF 项目迁移到基于 .NET Core 3&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可以持续关注官方 WPF on .NET Core 的例子：&lt;a href=&quot;https://github.com/dotnet/samples/tree/master/wpf/WPF-WinRT&quot;&gt;samples/wpf/WPF-WinRT at master · dotnet/samples&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 10:30:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-new-wpf-on-dotnet-core-project.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-new-wpf-on-dotnet-core-project.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Windows 的 UAC 设置中的通知等级实际上只有两个档而已</title>
        <description>&lt;p&gt;Windows 系统中的 UAC 设置界面有四种不同的选项可以选，但实际上真正有意义的只有两个选项。&lt;/p&gt;

&lt;p&gt;本文将介绍 UAC 这四个档设置的区别，帮助你合理的设置你的电脑。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;uac-设置界面&quot;&gt;UAC 设置界面&lt;/h2&gt;

&lt;p&gt;在 Windows 10 任务栏的搜索框中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;uac&lt;/code&gt; 可以直接打开 UAC 设置界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-16-40-47.png&quot; alt=&quot;搜索“更改用户账户控制设置”&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下图是“用户账户控制设置”界面，想必小伙伴们应该已经很熟悉了。它有四个档：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;始终通知&lt;/li&gt;
  &lt;li&gt;当应用试图安装软件或更改计算机设置时通知，使用安全桌面&lt;/li&gt;
  &lt;li&gt;当应用试图安装软件或更改计算机设置时通知，不使用安全桌面&lt;/li&gt;
  &lt;li&gt;从不通知&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-18-42-26.png&quot; alt=&quot;用户账户控制设置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;实际上只有两个档&quot;&gt;实际上只有两个档&lt;/h2&gt;

&lt;p&gt;然而在微软的 Raymond Chen（陈瑞孟）在 &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/?p=94105&quot;&gt;There are really only two effectively distinct settings for the UAC slider&lt;/a&gt; 一文中说实际上只有两个档：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;始终通知&lt;/li&gt;
  &lt;li&gt;辣鸡&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Windows 系统是通过让一些 UAC 提权动作变成静默提权的方式来避免通知过多的问题，主要是让那些“看起来没什么危害的”系统设置不用通知。但是，这相当于开了一个后门，程序可以很容易注入到 explorer.exe 中然后获得提权，或者通过白名单方式把自己加入到静默提权中。&lt;/p&gt;

&lt;p&gt;有了这个后门，大家就可以找到各种绕过 UAC 弹窗的方法，比如 &lt;a href=&quot;https://github.com/M2Team/NSudo&quot;&gt;NSudo&lt;/a&gt;、&lt;a href=&quot;https://github.com/hfiref0x/UACME&quot;&gt;UACME&lt;/a&gt;、QuickAdmin。你根本阻止不完这些绕过 UAC 弹窗的方法！&lt;/p&gt;

&lt;p&gt;微软说：“绕过 UAC 弹窗不是漏洞，所以我们不会修补。” &lt;em&gt;(也许将来绕过 UAC 弹窗的恶意软件泛滥的时候，微软就会做点什么了)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;微软已经提供了全部弹窗这个选项，明明可以阻止各类程序绕过 UAC，但为什么默认设置是这个可以绕过的选项呢？&lt;/p&gt;

&lt;p&gt;—— 因为&lt;strong&gt;用户&lt;/strong&gt;希望如此。&lt;/p&gt;

&lt;p&gt;Windows Vista 中，确实只有始终通知和关闭 UAC 两个选项，而且始终通知是默认选项；实际上 UAC 也确实只有这两个有实际意义的选项。但是始终通知会使得系统日常使用过程中真的有非常多的 UAC 弹窗，只要你试图修改一些可能影响其他用户的设置或者可能与 Windows 系统安全有关的操作，都会弹出 UAC 弹窗。大多数用户都会觉得这么多的 UAC 弹窗是很烦的。所以 Windows 7 开始不得不引入两个额外的中间状态，让一些已知的提权操作变成静默的，不弹 UAC 窗口。默认值是中间状态，因为大多数用户希望是这样的提醒级别。&lt;/p&gt;

&lt;h2 id=&quot;中间档的差别&quot;&gt;中间档的差别&lt;/h2&gt;

&lt;p&gt;进程在试图提权的时候，会弹出 UAC 提示。对于 Windows 管理员账户来说，在控制面板里面的大量操作可能都是在影响所有用户，如果全部通知，那么在控制面板里面点击的很多功能都会弹出 UAC 提示（例如修改时间，这是个影响所有用户的操作，而且有些安全软件可能会因为系统时间改变而失效）。&lt;/p&gt;

&lt;p&gt;那两个中间档就是指：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在控制面板里的管理操作不用弹出提示&lt;/li&gt;
  &lt;li&gt;在 Windows 资源管理器内部操作的时候不用弹出提示（启动子进程依然需要）&lt;/li&gt;
  &lt;li&gt;打开任务管理器的时候不用弹出提示&lt;/li&gt;
  &lt;li&gt;更改防火墙设置的时候不用弹出提示&lt;/li&gt;
  &lt;li&gt;打开 UAC 设置界面的时候不用弹出提示&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;我的建议&quot;&gt;我的建议&lt;/h2&gt;

&lt;p&gt;现在 Windows 10 都发布了很多个版本了，离 UAC 最初引入到 Windows 系统中时已经过去了十多年时间，这么长的时间，足够很多应用兼容 Medium 的权限级别了。&lt;/p&gt;

&lt;p&gt;如果你不了解 Medium 权限级别，可以阅读我的另一篇博客：&lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;即便我们现在选择“始终通知”，也不会比当初 Windows 7 刚刚发布时的通知多了，更不会比当初 Windows Vista 刚刚引入时多。因为应用的 UAC 弹窗少了，而对 Windows 的管理操作也不是经常进行。&lt;/p&gt;

&lt;p&gt;我现在日常使用的是“管理员账户 + 始终通知”，在某些情况下可能会使用“标准账户 + 始终通知”。并不会觉得多出了很多 UAC 弹窗。&lt;/p&gt;

&lt;p&gt;目前感觉最明显的多出来的弹窗是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;打开任务管理器的时候会弹窗&lt;/li&gt;
  &lt;li&gt;添加防火墙信任的时候会弹窗&lt;/li&gt;
  &lt;li&gt;在资源管理器中修改系统目录的时候会弹窗&lt;/li&gt;
  &lt;li&gt;在 Windows 设置应用中的一些设置会弹窗&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多关于-uac-的博客&quot;&gt;更多关于 UAC 的博客&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/requested-execution-level-of-application-manifest&quot;&gt;应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-with-lowered-uac-privileges&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-in-a-specific-trust-level&quot;&gt;Windows 下使用 runas 命令以指定的权限启动一个进程（非管理员、管理员） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/?p=94105&quot;&gt;The Old New Thing - There are really only two effectively distinct settings for the UAC slider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 09:43:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/there-are-only-two-settings-for-the-uac-slider.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/there-are-only-two-settings-for-the-uac-slider.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Windows 中的 UAC 用户账户控制</title>
        <description>&lt;p&gt;阅读本文，你可以初步了解 Windows 上的 UAC 用户账户控制机制。本文不会涉及到 UAC 的底层实现原理和安全边界问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;用户账户&quot;&gt;用户账户&lt;/h2&gt;

&lt;p&gt;在 Windows 中有多种不同的账户：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SYSTEM&lt;/li&gt;
  &lt;li&gt;Administrators 用户组
    &lt;ul&gt;
      &lt;li&gt;Administrator&lt;/li&gt;
      &lt;li&gt;管理员账户&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Users 用户组
    &lt;ul&gt;
      &lt;li&gt;标准账户&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们需要将这些账户列举出来是因为在解释 UAC 账户控制的时候，会与此相关。&lt;/p&gt;

&lt;p&gt;SYSTEM 在系统中拥有最高权限。&lt;/p&gt;

&lt;p&gt;默认我们安装 Windows 时会创建一个管理员账户，这也是 Windows 系统推荐我们使用的管理员账户，其权限等级比 SYSTEM 低。&lt;/p&gt;

&lt;p&gt;Administrator 的权限级别和我们用户创建的管理员账户的权限级别是一样的，但是访问令牌（Access Token）的管理方式不一样，所以这里我们需要分开说。&lt;/p&gt;

&lt;p&gt;标准账户是我推荐大家使用的首选账户种类，因为在普通使用场景下，这个是最安全的。&lt;/p&gt;

&lt;p&gt;Administrator 账户目前的主要作用就是准备 OOBE 开箱体验，不适合日常使用，因为很不安全。关于 OOBE 开箱体验与审核模式，可以阅读我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-audit-mode&quot;&gt;启用 Windows 审核模式（Audit Mode），以 Administrator 账户来设置电脑的开箱体验 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;uac-通知等级&quot;&gt;UAC 通知等级&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-18-42-26.png&quot; alt=&quot;用户账户控制设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Windows Vista 开始引入了 UAC，不过在 Windows Vista 上只有两种 UAC 设置——开启和关闭。如果开启，那么应用试图安装软件或更改计算机、或者更改了 Windows 设置时将弹出 UAC 提示框；如果关闭，那么 UAC 就此关闭。Windows Vista 的 UAC 一直饱受诟病就是因为这种情况下的 UAC 提示是非常频繁的（而且以前的程序迁移到不需要管理员权限需要时间）。&lt;/p&gt;

&lt;p&gt;在 Windows 7 上，在开启和关闭中间新引入了两个 UAC 级别，都是在更改 Windows 设置时不通知（实际上就是加了一些 UAC 提权的白名单）。只是一个会进入“黑屏”状态，另一个不会进入此状态。从表现上看这两个只是黑屏与不黑屏，但从安全性上讲黑屏的安全性会高很多。UAC 通知时进入的黑屏状态在 Windows 中称之为“安全桌面”，这时整个桌面进入了 SYSTEM 账户，原用户账户下的所有程序都无法得知此时 UAC 弹窗的情况，也无法通过模拟用户操作来跳过这个 UAC 框。而不黑屏时，不会切换到新的桌面环境，原有程序依然可以获得此 UAC 弹窗的一些信息，这很不安全。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;但是&lt;/strong&gt;！无论是 Windows Vista 还是 Windows 7，一旦你将 UAC 设置拖到最底，那么此时 UAC 将彻底关闭。如果你是管理员账户，那么运行的程序都将以管理员权限运行。&lt;/p&gt;

&lt;p&gt;从 Windows 8 开始到现在的 Windows 10，虽然依然是上面四个设置，但拖到最底的“从不通知”时，UAC 依然是开启的状态。也就是说，用户正常启动的进程依然是标准权限，要获得管理员权限提升依然需要重启整个进程。这个安全性限制是很重要的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;特别说明&lt;/strong&gt;！实际上 UAC 拖到最顶部，也就是所有 UAC 通知都显示 UAC 提示窗口才是真的在利用 UAC 保护你的电脑。因为 Windows 7 开始新增的两个中间级别都是在部分情况下静默提权，而这两种级别因为可以静默提权，所以也可以很容易被程序绕过。微软认为绕过 UAC 弹窗不是漏洞，因为这是用户自己的选择——如果用户选择全部通知是不会绕过的，用户选择了默认值，于是才可以绕过。所以这里推荐大家使用 UAC 的最高档，也就是全部提权都通知，这可以让大多数绕过 UAC 的方法失效。&lt;/p&gt;

&lt;p&gt;虽然说通知等级给了用户四个设置项，但实际上真正有用的只有两个而已，参见我的另一篇博客：&lt;a href=&quot;/post/there-are-only-two-settings-for-the-uac-slider&quot;&gt;Windows 的 UAC 设置中的通知等级实际上只有两个档而已 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;完整性级别integrity-level&quot;&gt;完整性级别（Integrity Level）&lt;/h2&gt;

&lt;p&gt;从 Windows Vista 开始，进程在创建的时候，可以得到一个访问令牌（Access Token），这个令牌有四个完整性级别：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;System（系统）&lt;/li&gt;
  &lt;li&gt;High（高）&lt;/li&gt;
  &lt;li&gt;Medium（中）&lt;/li&gt;
  &lt;li&gt;Low（低）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;System 令牌是对系统完全操作的令牌，对应 SYSTEM 用户拥有的最高权限，可以对 Windows 操作系统做任何事。通常一个服务进程会以 SYSTEM 用户启动，拿到 System 令牌。&lt;/p&gt;

&lt;p&gt;High 对应 Administrators 组拥有的最高权限，也就是前面所说的 Administrator 用户和用户自己创建的管理员账户的权限级别。此权限级别用来管理计算机，可以修改其他用户，可以修改系统的设置，这些设置可能会造成安全问题（比如更改系统时间可能导致杀毒软件失效）。&lt;/p&gt;

&lt;p&gt;Medium 对应 Users 组拥有的最高权限，也就是前面所说的用户自己创建的标准用户。此权限级别用来日常使用。Medium 权限在 Windows Vista（实际上是其内核 NT6）中相比于之前版本的 Windows 有一些权限的提升，不危及系统安全性的操作在 Medium 下即可以完成，不需要切换到 High 级别。Users 组的用户是没有 High 和 System 令牌的，程序在此用户账户下，无论如何也无法拿到 High 和 System 令牌的，因为这个用户没有这样的令牌；如果要权限提升，需要输入管理员账号密码，而这时拿到的是这个管理员账号的 High 和 System 令牌。&lt;/p&gt;

&lt;p&gt;Low 并不对应者一个用户组，这是为了一些需要特殊保护的应用程序准备的。有些应用容易受到攻击，那么使用 Low 令牌启动这些应用程序，可以最大程度减少利用这些应用对系统造成攻击。比如 IE 浏览器的页面进程使用 Low 令牌运行，其对系统很难做出什么改动，甚至也影响不了当前用户的文件；当需要需系统计算机进行交互的时候，会与 IE 的 UI 进程（Medium 令牌）进行通信，请求协助完成。&lt;/p&gt;

&lt;p&gt;当 UAC 是开启状态，无论是管理员账户还是标准账户，Windows 资源管理器进程（explorer.exe）都是以 Medium 令牌启动进程。由于子进程通常能够继承父进程的令牌完整性级别，所以这样的设定可以防止用户双击打开的程序得到过高的令牌，从而在用户不知情的情况下危及系统安全。&lt;/p&gt;

&lt;p&gt;当程序需要以管理员权限运行（对应 High 级别的令牌）时，可以自己在 Manifest 里面声明，也可以自己使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;runas&lt;/code&gt; 谓词重启自己。而这个时候是会弹出 UAC 提示的，用户知情。&lt;/p&gt;

&lt;p&gt;前面我们说过在 Administrators 组中，Administrator 账户和普通管理员账户要分开说。差别就在令牌的管理上。普通管理员账户下，正常启动进程使用的是继承自 explorer.exe 的 Medium 访问令牌，当进程需要提升权限时，会弹出 UAC 提示框来启动一个子进程以获得 High 令牌。而 Administrator 账户下，正常启动的进程也都获得了 High 令牌。&lt;/p&gt;

&lt;p&gt;关于如何通过 Manifest 设置管理员权限运行，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/requested-execution-level-of-application-manifest&quot;&gt;应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;权限提升&quot;&gt;权限提升&lt;/h2&gt;

&lt;p&gt;在 Windows 系统中，不同权限的进程是隔离的（虽然不是完全隔离）。&lt;/p&gt;

&lt;p&gt;如果你希望你的程序在执行某个操作的时候提升权限来执行，实际上你不能在你原来的进程上直接提升权限。你有很多种方法来提权，甚至绕过 UAC 来提权，但无论哪一种，你的进程实际上都是重启了，你是在新的提升的进程中执行了这个需要权限的操作。&lt;/p&gt;

&lt;p&gt;对于管理员账户，如果启动一个普通进程，那么此进程在管理员账户下运行，获得的是 Medium 访问令牌。当此进程提升权限，将弹出 UAC 提示框，用户同意后继续使用此同一个管理员账户运行，但子进程将获得 High 访问令牌。&lt;/p&gt;

&lt;p&gt;对于标准账户，如果启动一个普通进程，那么此进程在标准账户下运行，获得的是 Medium 访问令牌。当此进程提升权限，将弹出 UAC 提示框，用户输入管理员账号密码后，子进程将在输入的管理员账户下运行，获得此管理员的 High 访问令牌。&lt;strong&gt;标准账户没有 High 访问令牌&lt;/strong&gt;，如果说绕过 UAC 来提权是为了获取 High 访问令牌，那么在标准账户下根本没有 High 访问令牌，所以你绕不过。&lt;/p&gt;

&lt;p&gt;管理员账户的 UAC 弹窗是这样的，要求用户选“是”或者“否”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-16-42-45.png&quot; alt=&quot;UAC 弹窗&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而标准账户的 UAC 弹窗是这样的，要求输入管理员账号和密码：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-19-21-44.png&quot; alt=&quot;UAC 输入账号密码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上两个弹窗都是蓝色的，代表发起此 UAC 请求的子进程其程序的证书是经过认证的。如果没有证书那么提示框是黄色的，如果证书过期，那么提示框是红色的。这可以帮助用户区分 UAC 弹窗做出决策（虽然实际上没什么用）。&lt;/p&gt;

&lt;p&gt;以上在标准账户下用管理员账户打开子进程的例子可以看下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-16-57-48.png&quot; alt=&quot;标准账户下运行管理员权限程序会切换账户&quot; /&gt;&lt;/p&gt;

&lt;p&gt;lvyi 是我安装系统时创建的管理员账号，但是我使用的是 walterlv 标准账号。正常是在 walterlv 账号下启动程序，但以管理员权限运行时，会要求输入 lvyi 账号的密码来提权，于是就会以 lvyi 的身份运行这个程序。这种情况下，那个管理员权限运行的程序会以为当前运行在 lvyi 这个账户下，程序员需要小心这里的坑，因为拿到的用户路径以及注册表不是你所期望的 walterlv 这个账号下的。&lt;/p&gt;

&lt;p&gt;在上图中，你会发现当前账户下的任务管理器连管理员账户运行的程序图标都拿不到。&lt;/p&gt;

&lt;!-- ---

**参考资料**

- [Windows 7 中的用户帐户控制（UAC）真的有必要吗？ - 洛晓晓晓晓的回答 - 知乎](https://www.zhihu.com/question/20139121/answer/57630581) --&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 09:42:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-user-account-control.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-user-account-control.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Windows 下使用 runas 命令以指定的权限启动一个进程（非管理员、管理员）</title>
        <description>&lt;p&gt;在默认情况下，Windows 系统中启动一个进程会继承父进程的令牌。如果父进程是管理员权限，那么子进程就是管理员权限；如果父进程是标准用户权限，那么子进程也是标准用户权限。&lt;/p&gt;

&lt;p&gt;我们也知道，可以使用一些方法为自己的应用程序提权。但是有没有方法可以任意指定一个权限然后运行呢？本文将介绍 Windows 下指定权限运行的做法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;runas-命令&quot;&gt;runas 命令&lt;/h2&gt;

&lt;p&gt;runas 是 Windows 系统上自带的一个命令，通过此命令可以以指定权限级别间接启动我们的程序，而不止是继承父进程的权限。&lt;/p&gt;

&lt;p&gt;打开 cmd 或者 PowerShell，输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;runas&lt;/code&gt; 命令可以看到其用法。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RUNAS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用法&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RUNAS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noprofile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/savecred&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/netonly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/user:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UserName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RUNAS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noprofile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/savecred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/smartcard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/user:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UserName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RUNAS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/trustlevel:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TrustLevel&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noprofile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定不应该加载用户的配置文件。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;这会加速应用程序加载，但&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可能会造成一些应用程序运行不正常。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/profile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定应该加载用户的配置文件。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;这是默认值。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/env&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;要使用当前环境，而不是用户的环境。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/netonly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;只在指定的凭据限于远程访问的情况下才使用。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/savecred&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;用用户以前保存的凭据。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/smartcard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;如果凭据是智能卡提供的，则使用这个选项。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UserName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;应使用&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DOMAIN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;或&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DOMAIN\USER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;形式&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/showtrustlevels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;显示可以用作&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/trustlevel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的参数的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;信任级别。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/trustlevel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;应该是在&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/showtrustlevels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中枚举&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的一个级别。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;EXE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的命令行。请参阅下面的例子&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;示例&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noprofile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/user:mymachine\administrator&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/profile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/env&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/user:mydomain\admin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mmc %windir%\system32\dsa.msc&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/env&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/user:user&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;domain.microsoft.com&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;notepad \&quot;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file.txt\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;注意&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;只在得到提示时才输入用户的密码。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;注意&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/profile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;跟&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/netonly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;不兼容。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;注意&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/savecred&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;跟&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/smartcard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;不兼容。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;提权运行或者降权运行&quot;&gt;提权运行或者降权运行&lt;/h2&gt;

&lt;p&gt;为了演示提权或者降权，我们需要有一个能够验证当前是否是管理员权限运行的程序。关于如何在程序中判断当前是否以管理员权限运行，可以阅读我和林德熙的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/dotnet-%E5%88%A4%E6%96%AD%E7%A8%8B%E5%BA%8F%E5%BD%93%E5%89%8D%E4%BD%BF%E7%94%A8%E7%AE%A1%E7%90%86%E5%91%98%E8%BF%90%E8%A1%8C%E9%99%8D%E4%BD%8E%E6%9D%83%E4%BD%BF%E7%94%A8%E6%99%AE%E9%80%9A%E6%9D%83%E9%99%90%E8%BF%90%E8%A1%8C.html&quot;&gt;dotnet 判断程序当前使用管理员运行降低权使用普通权限运行 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-with-lowered-uac-privileges&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本质上是这段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowsIdentity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowsPrincipal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowsBuiltInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Administrator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 检测到当前进程是以管理员权限运行的。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此代码如果在 .NET Core 中编写，以上代码需要额外安装 Windows 兼容包：&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Windows.Compatibility&quot;&gt;Microsoft.Windows.Compatibility&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;提权运行或者降权运行-1&quot;&gt;提权运行或者降权运行&lt;/h2&gt;

&lt;p&gt;我以标准用户权限和管理员权限分别启动了一个 PowerShell Core，然后准备在这两个窗口里面分别启动我的检测管理员权限的程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-09-21-30.png&quot; alt=&quot;在两个 PowerShell 中运行命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;0x20000 是标准用户权限，现在运行命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/trustlevel:0x20000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Demo.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-09-24-00.png&quot; alt=&quot;运行结束后，两个进程都是非管理员权限&quot; /&gt;&lt;/p&gt;

&lt;p&gt;运行发现，两个进程现在都是标准用户权限。即使是管理员的 PowerShell 中运行的也都是非管理员权限。&lt;/p&gt;

&lt;p&gt;0x40000 是管理员权限，现在运行命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/trustlevel:0x40000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Demo.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-19-09-26-28.png&quot; alt=&quot;运行结束后，两个进程都取得不高于当前 PowerShell 的最高权限&quot; /&gt;&lt;/p&gt;

&lt;p&gt;运行发现，非管理员的 PowerShell 启动的是非管理员权限的进程；而管理员的 PowerShell 启动的是管理员权限的进程。&lt;/p&gt;

&lt;h2 id=&quot;使用-c-代码来降权运行&quot;&gt;使用 C# 代码来降权运行&lt;/h2&gt;

&lt;p&gt;使用 C# 代码，就是要将下面这一句翻译成 C#。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/trustlevel:0x20000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Walterlv.Demo.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以其实非常简单，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.Start&lt;/code&gt; 传入参数即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;runas.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;/trustlevel:0x20000 Walterlv.Demo.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于更多降权运行的方法，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-with-lowered-uac-privileges&quot;&gt;在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/29570173/6233938&quot;&gt;windows - How to run a process as non-admin from an elevated PowerShell console? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 01:37:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/start-process-in-a-specific-trust-level.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/start-process-in-a-specific-trust-level.html</guid>
        
        
        <category>windows</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在 Windows 系统上降低 UAC 权限运行程序（从管理员权限降权到普通用户权限）</title>
        <description>&lt;p&gt;在 Windows 系统中，管理员权限和非管理员权限运行的程序之间不能使用 Windows 提供的通信机制进行通信。对于部分文件夹（ProgramData），管理员权限创建的文件是不能以非管理员权限修改和删除的。&lt;/p&gt;

&lt;p&gt;然而，一个进程运行之后启动的子进程，会&lt;strong&gt;继承当前进程的 UAC 权限&lt;/strong&gt;；于是有时我们会有降权运行的需要。本文将介绍 Windows 系统上降权运行的几种方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文的降权运行指的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有一个 A 程序是以管理员权限运行的（典型的，如安装包）；&lt;/li&gt;
  &lt;li&gt;有一个 B 程序会被 A 启动（我们期望降权运行的 B 程序）。&lt;/li&gt;
&lt;/ol&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何判断当前进程的-uac-权限&quot;&gt;如何判断当前进程的 UAC 权限&lt;/h2&gt;

&lt;p&gt;通过下面的代码，可以获得当前进程的 UAC 权限。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowsIdentity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowsPrincipal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而如果要判断是否是管理员权限，则使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowsBuiltInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Administrator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 当前正在以管理员权限运行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此代码如果在 .NET Core 中编写，需要额外安装 Windows 兼容包：&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Windows.Compatibility&quot;&gt;Microsoft.Windows.Compatibility&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;方法一使用-runas-命令来运行程序推荐&quot;&gt;方法一：使用 runas 命令来运行程序（推荐）&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;runas&lt;/code&gt; 命令来运行，可以指定一个权限级别：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;runas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/trustlevel:0x20000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Users\walterlv\Desktop\walterlv.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subProcessFileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;C:\Users\walterlv\Desktop\walterlv.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;runas.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;/trustlevel:0x20000 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subProcessFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于 runas 的更多细节，可以参考我的另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/start-process-in-a-specific-trust-level&quot;&gt;Windows 下使用 runas 命令以指定的权限启动一个进程（非管理员、管理员） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;方法二使用-explorerexe-代理运行程序&quot;&gt;方法二：使用 explorer.exe 代理运行程序&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;请特别注意&lt;/strong&gt;，使用 explorer.exe 代理运行程序的时候，是不能带参数的，否则 explorer.exe 将不会启动你的程序。&lt;/p&gt;

&lt;p&gt;因为绝大多数用户启动系统的时候，explorer.exe 进程都是处于运行状态，而如果启动一个新的 explorer.exe，都会自动激活当前正在运行的进程而不会启动新的。&lt;/p&gt;

&lt;p&gt;于是我们可以委托默认以普通权限运行的 explorer.exe 来代理启动我们需要启动的子进程，这时启动的子进程便是与 explorer.exe 相同权限的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subProcessFileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;C:\Users\walterlv\Desktop\walterlv.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;explorer.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subProcessFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果用户计算机上的 UAC 是打开的，那么 explorer.exe 默认就会以标准用户权限运行。通过以上代码，&lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.exe&lt;/code&gt; 就会以与 explorer.exe 相同权限运行，也就是降权运行了。&lt;/p&gt;

&lt;p&gt;不过值得注意的是，Windows 7 上控制面板的 UAC 设置拉倒最低就是关掉 UAC 了；Windows 8 开始拉倒最底 UAC 还是打开的，只是不会提示 UAC 弹窗而已。也就是说，拉倒最底的话，Windows 7 的 UAC 就会关闭，explorer.exe 就会以管理员权限启动。&lt;/p&gt;

&lt;p&gt;下面的代码，如果发现自己是以管理员权限运行的，那么就降权重新运行自己，然后自己退出。（当然在关闭 UAC 的电脑上是无效的。）&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowsIdentity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowsPrincipal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;principal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowsBuiltInRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Administrator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 检测到当前进程是以管理员权限运行的，于是降权启动自己之后，把自己关掉。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;explorer.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Shutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;请再次特别注意&lt;/strong&gt;，使用 explorer.exe 代理运行程序的时候，是不能带参数的，否则 explorer.exe 将不会启动你的程序。&lt;/p&gt;

&lt;h2 id=&quot;方法三在启动进程时传入用户名和密码&quot;&gt;方法三：在启动进程时传入用户名和密码&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessStartInfo&lt;/code&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;UserName&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Password&lt;/code&gt; 属性，设置此属性可以以此计算机上的另一个用户身份启动此进程。如果这个用户是普通用户，那么就会以普通权限运行此进程。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProcessStartInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Verb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;runas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UserName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadPassword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UseShellExecute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LoadUserProfile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadPassword&lt;/code&gt; 函数来自我的另一篇博客：&lt;a href=&quot;/post/input-password-with-mask-in-cli&quot;&gt;如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;然而，此方法最大的问题在于——产品级的程序，不可能也不应该知道用户的密码！所以实际上这样的方法并不实用。&lt;/p&gt;

&lt;h2 id=&quot;方法四使用-shell-进程的-access-token-来启动进程&quot;&gt;方法四：使用 Shell 进程的 Access Token 来启动进程&lt;/h2&gt;

&lt;p&gt;此方法需要较多的 Windows API 调用，我没有尝试过这种方法，但是你可以自行尝试下面的链接：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/49997055/6233938&quot;&gt;c# - How do you de-elevate privileges for a child process - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/7870319/6233938&quot;&gt;c# starting process with lowered privileges from UAC admin level process - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/1173630/6233938&quot;&gt;c# - How do you de-elevate privileges for a child process - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/49997055/6233938&quot;&gt;c# - How do you de-elevate privileges for a child process - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/q/171917/940098&quot;&gt;windows - Force a program to run &lt;em&gt;without&lt;/em&gt; administrator privileges or UAC? - Super User&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/18946/High-elevation-can-be-bad-for-your-application-How&quot;&gt;High elevation can be bad for your application: How to start a non-elevated process at the end of the installation - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://helgeklein.com/blog/2010/03/how-to-enable-drag-and-drop-for-an-elevated-mfc-application-on-vistawindows-7/&quot;&gt;How to Enable Drag and Drop for an Elevated MFC Application on Vista/Windows 7 • Helge Klein&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Mar 2019 01:36:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/start-process-with-lowered-uac-privileges.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/start-process-with-lowered-uac-privileges.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>启用 Windows 审核模式（Audit Mode），以 Administrator 账户来设置电脑的开箱体验</title>
        <description>&lt;p&gt;在你刚刚安装完 Windows，在 Windows 开箱体验输入以创建你的用户账户之前，你可以按下 Ctrl + Shift + F3 来进入审核模式。&lt;/p&gt;

&lt;p&gt;本文将介绍审核模式。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;oobe&quot;&gt;OOBE&lt;/h2&gt;

&lt;p&gt;OOBE，Out-of-Box Experience，开箱体验。对于 Windows 系统来说，就是当你买下电脑回来，兴奋地打开电脑开机后第一个看到的界面。&lt;/p&gt;

&lt;p&gt;具体来说，就是设置你的账号以及各种个性化设置的地方。&lt;/p&gt;

&lt;p&gt;本文即将要说的审核模式就是在这里开启的。&lt;em&gt;当然你设置完账号也一样能开启，但开箱就是要来个干净整洁嘛，所以就是应该在还没有账号的时候进入审核模式。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;进入审核模式&quot;&gt;进入审核模式&lt;/h2&gt;

&lt;p&gt;在 OOBE 界面中，按下 Ctrl + Shift + F3 两次即会进入审核模式。&lt;/p&gt;

&lt;p&gt;实际上此时进入的账号是 Administrator 账号。我在 &lt;a href=&quot;/post/windows-user-account-control&quot;&gt;Windows 中的 UAC 用户账户控制&lt;/a&gt; 一文中说到，Administrator 账号下启动进程获取到的访问令牌都是完全访问令牌。所以在这里 UWP 程序是无法运行的（逃&lt;/p&gt;

&lt;p&gt;当你进入审核模式之后，会看到自动启动了一个 sysprep 的程序，它位于 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\System32\Sysprep&lt;/code&gt; 目录下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-20-00-59.png&quot; alt=&quot;系统准备工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在审核模式下，重启也会继续进入审核模式。如果要关闭审核模式，则需要在 sysprep 程序中把下一次的启动选项改为开箱体验。&lt;/p&gt;

&lt;p&gt;关于清理选项中的“通用”：如果你只为这台电脑或这个型号的电脑设置开箱体验，那么就关闭“通用”；如果把这个开箱体验做好之后会拷贝副本到其他型号的电脑上，那么就勾选“通用”。区别就是是否清理掉设备的特定的驱动文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-20-01-53.png&quot; alt=&quot;清理 - 通用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，你现在就可以去 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\System32\Sysprep&lt;/code&gt; 目录中启动 sysprep.exe，然后给你的电脑再带来一次开箱体验。&lt;/p&gt;

&lt;h2 id=&quot;审核模式有什么作用&quot;&gt;审核模式有什么作用？&lt;/h2&gt;

&lt;p&gt;从进入审核模式时打开的 sysprep.exe 程序可以看出来，这个模式主要就是为了准备开箱体验的。&lt;/p&gt;

&lt;p&gt;你可以在这里以 Administrator 权限来为此计算机安装驱动，为将来此计算机的所有用户安装应用、存放一些你认为他们需要的文件。而这一切操作都不需要特地创建一个账号。可以说 Administrator 账户内置到系统里，主要的目的就是这个了，临时使用。而目前就是在审核模式中制作开箱体验。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/boot-windows-to-audit-mode-or-oobe&quot;&gt;Boot Windows to Audit Mode or OOBE - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 17 Mar 2019 12:02:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-audit-mode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-audit-mode.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何创建应用程序清单文件 App.Manifest，如何创建不带清单的应用程序</title>
        <description>&lt;p&gt;如果你的程序对 Windows 运行权限有要求，那么需要设置应用程序清单。本文介绍如何添加应用程序清单，并解释其中各项权限设置的实际效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;嵌入带默认设置的清单&quot;&gt;嵌入带默认设置的清单&lt;/h2&gt;

&lt;p&gt;对于 WPF 和 Windows Forms 程序，如果你什么都不做，那么就已经嵌入了一个带有默认设置的清单。&lt;/p&gt;

&lt;p&gt;下图可以在 Visual Studio 中的项目上右键属性插件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-15-40-41.png&quot; alt=&quot;嵌入带默认设置的清单&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;新建一个自定义的清单文件&quot;&gt;新建一个自定义的清单文件&lt;/h2&gt;

&lt;p&gt;在项目上右键，添加，新建项。可以在新建模板中找到“应用程序清单文件”。确认后即添加了一个新的清单文件。这时，项目属性页中的清单也会自动设置为刚刚添加的清单文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-15-42-37.png&quot; alt=&quot;按照清单模板新建清单&quot; /&gt;&lt;/p&gt;

&lt;p&gt;默认的清单中，包含 UAC 清单选项、系统兼容性选项、DPI 感知级别选项和 Windows 公共控件和对话框的主题选项。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;关于 UAC 清单选项，你可以阅读 &lt;a href=&quot;/post/requested-execution-level-of-application-manifest&quot;&gt;应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果&lt;/a&gt; 了解更多。&lt;/li&gt;
  &lt;li&gt;关于 DPI 感知级别选项，你可以阅读 &lt;a href=&quot;/post/windows-high-dpi-development&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - walterlv&lt;/a&gt; 和 &lt;a href=&quot;/post/windows-high-dpi-development-for-wpf&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发 - walterlv&lt;/a&gt; 了解更多。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;assembly&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;manifestVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;assemblyIdentity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyApplication.app&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;trustInfo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;security&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;requestedPrivileges&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- UAC 清单选项
             如果想要更改 Windows 用户帐户控制级别，请使用
             以下节点之一替换 requestedExecutionLevel 节点。n
        &amp;lt;requestedExecutionLevel  level=&quot;asInvoker&quot; uiAccess=&quot;false&quot; /&amp;gt;
        &amp;lt;requestedExecutionLevel  level=&quot;requireAdministrator&quot; uiAccess=&quot;false&quot; /&amp;gt;
        &amp;lt;requestedExecutionLevel  level=&quot;highestAvailable&quot; uiAccess=&quot;false&quot; /&amp;gt;

            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
            如果你的应用程序需要此虚拟化来实现向后兼容性，则删除此
            元素。
        --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;requestedExecutionLevel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;asInvoker&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uiAccess=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/requestedPrivileges&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/security&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/trustInfo&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;compatibility&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:compatibility.v1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;application&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
           Windows 版本的列表。取消评论适当的元素，
           Windows 将自动选择最兼容的环境。 --&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Windows Vista --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;supportedOS Id=&quot;{e2011457-1546-43c5-a5fe-008deee3d3f0}&quot; /&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Windows 7 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;supportedOS Id=&quot;{35138b9a-5d96-4fbd-8e2d-a2440225f93a}&quot; /&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Windows 8 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;supportedOS Id=&quot;{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}&quot; /&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Windows 8.1 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;supportedOS Id=&quot;{1f676c76-80e1-4239-95bb-83d0f6d0da78}&quot; /&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Windows 10 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;supportedOS Id=&quot;{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}&quot; /&amp;gt;--&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/compatibility&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 指示该应用程序可以感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI，无需
       选择加入。选择加入此设置的 Windows 窗体应用程序(目标设定为 .NET Framework 4.6 )还应
       在其 app.config 中将 &quot;EnableWindowsFormsHighDpiAutoResizing&quot; 设置设置为 &quot;true&quot;。--&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!--
  &amp;lt;application xmlns=&quot;urn:schemas-microsoft-com:asm.v3&quot;&amp;gt;
    &amp;lt;windowsSettings&amp;gt;
      &amp;lt;dpiAware xmlns=&quot;http://schemas.microsoft.com/SMI/2005/WindowsSettings&quot;&amp;gt;true&amp;lt;/dpiAware&amp;gt;
    &amp;lt;/windowsSettings&amp;gt;
  &amp;lt;/application&amp;gt;
  --&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!--
  &amp;lt;dependency&amp;gt;
    &amp;lt;dependentAssembly&amp;gt;
      &amp;lt;assemblyIdentity
          type=&quot;win32&quot;
          name=&quot;Microsoft.Windows.Common-Controls&quot;
          version=&quot;6.0.0.0&quot;
          processorArchitecture=&quot;*&quot;
          publicKeyToken=&quot;6595b64144ccf1df&quot;
          language=&quot;*&quot;
        /&amp;gt;
    &amp;lt;/dependentAssembly&amp;gt;
  &amp;lt;/dependency&amp;gt;
  --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/assembly&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建不带清单的应用程序&quot;&gt;创建不带清单的应用程序&lt;/h2&gt;

&lt;p&gt;你也可以创建一个不带应用程序清单的应用程序。方法是在属性页中将清单设置为“创建不带清单的应用程序”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-17-15-48-41.png&quot; alt=&quot;创建不带清单的应用程序&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 17 Mar 2019 09:34:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-manifest-file-for-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-manifest-file-for-application.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AutoResetEvent&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ManualResetEvent&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Monitor&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt; 等等这些用来做同步的类，如果在异步上下文（await）中使用，需要非常谨慎。&lt;/p&gt;

&lt;p&gt;本文将说一个在同步上下文中非常常见的一种用法，换成异步上下文中会产生死锁的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一段正常的同步上下文的代码&quot;&gt;一段正常的同步上下文的代码&lt;/h2&gt;

&lt;p&gt;先看看一段非常简单的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ThreadPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetMinThreads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 全部在后台线程，不会死锁。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 主线程执行与后台线程并发竞争，也不会死锁。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这个 ++ 在安全的线程上下文中，所以不需要使用 Interlocked.Increment(ref _count);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码运行会输出 200 个 “walterlv is a 逗比”：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 有 200 个，但是不需要再在这里占用行数了。[197] walterlv is a 逗比&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;逗比&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码最关键的使用锁进行同步的地方是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Do&lt;/code&gt; 函数，采用了非常典型的防止方法重入的措施：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 获得锁&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 执行某个需要线程安全的操作。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 释放锁&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们设置了线程池最小线程数为 100，这样在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 进行并发的时候，一次能够开启 100 个线程来执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Do&lt;/code&gt; 方法。同时 UI 线程也执行 100 次，与后台线程竞争输出。&lt;/p&gt;

&lt;h2 id=&quot;一个微调即会死锁&quot;&gt;一个微调即会死锁&lt;/h2&gt;

&lt;p&gt;现在我们微调一下刚刚的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ThreadPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetMinThreads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 全部在后台线程，不会死锁。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 主线程执行与后台线程并发竞争，也不会死锁。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoCoreAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoCoreAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了直观看出差别，我只贴出不同之处：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        {
&lt;span class=&quot;gd&quot;&gt;--          Task.Run(() =&amp;gt; Do());
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          Task.Run(() =&amp;gt; DoAsync());
&lt;/span&gt;        }
    ...
        {
&lt;span class=&quot;gd&quot;&gt;--          Do();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          DoAsync();
&lt;/span&gt;        }

--  private void Do()
&lt;span class=&quot;gi&quot;&gt;++  private async Task DoAsync()
&lt;/span&gt;    {
    ...
            _count++;
&lt;span class=&quot;gd&quot;&gt;--          await DoCore();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++          await DoCoreAsync();
&lt;/span&gt;        }
    ...
    }

--  private void DoCore()
&lt;span class=&quot;gi&quot;&gt;++  private async Task DoCoreAsync()
&lt;/span&gt;    {
&lt;span class=&quot;gd&quot;&gt;--      Console.WriteLine($&quot;[{_count.ToString().PadLeft(3, ' ')}] walterlv is a 逗比&quot;);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      await Task.Run(async () =&amp;gt;
++      {
++          Console.WriteLine($&quot;[{_count.ToString().PadLeft(3, ' ')}] walterlv is a 逗比&quot;);
++      });
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在再运行代码，只输出几次程序就停下来了：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[  0] walterlv is a 逗比
[  1] walterlv is a 逗比
[  2] walterlv is a 逗比
[  3] walterlv is a 逗比
[  4] walterlv is a 逗比
[  5] walterlv is a 逗比
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每次运行时，停下来的次数都不相同，这也正符合多线程坑的特点。&lt;/p&gt;

&lt;h2 id=&quot;此死锁的触发条件&quot;&gt;此死锁的触发条件&lt;/h2&gt;

&lt;p&gt;实际上，以上这段代码如果没有 WPF / UWP 的 UI 线程的参与，是 &lt;strong&gt;不会出现死锁&lt;/strong&gt; 的。&lt;/p&gt;

&lt;p&gt;但是，如果有 UI 线程参与，即便只有 UI 线程调用，也会直接死锁。例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只是这样的调用，你会看到值输出一次 —— 这就已经死锁了！&lt;/p&gt;

&lt;h2 id=&quot;此死锁的原因&quot;&gt;此死锁的原因&lt;/h2&gt;

&lt;p&gt;WPF / UWP 等 UI 线程会使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt; 作为线程同步上下文，我在 &lt;a href=&quot;/post/yield-in-task-dispatcher&quot;&gt;出让执行权：Task.Yield, Dispatcher.Yield - walterlv&lt;/a&gt; 一问中有说到它的原理。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等待完成之后，会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt; 回到 UI 线程。然而，此时 UI 线程正卡死在 &lt;code class=&quot;highlighter-rouge&quot;&gt;_resetEvent.WaitOne();&lt;/code&gt;，于是根本没有办法执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt; 中的操作，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 之后的代码。然而释放锁的代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;_resetEvent.Set();&lt;/code&gt; 就在 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 之后，所以不会执行，于是死锁。&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deadlock-if-await-in-ui-lock-context.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deadlock-if-await-in-ui-lock-context.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况</title>
        <description>&lt;p&gt;一个简单的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 不会消耗多少时间，但如果你不合适地将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 转为同步等待，那么也可能很快耗尽线程池的所有资源，出现类似死锁的情况。&lt;/p&gt;

&lt;p&gt;本文将以一个最简单的例子说明如何出现以及避免这样的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;耗时的-taskrun&quot;&gt;耗时的 Task.Run&lt;/h2&gt;

&lt;p&gt;谁都不会认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run(() =&amp;gt; 1)&lt;/code&gt; 这个异步任务执行会消耗多少时间。&lt;/p&gt;

&lt;p&gt;但实际上，如果你的代码写得不清真，它真的能消耗大量的时间，这种时间消耗有点像死锁。&lt;/p&gt;

&lt;p&gt;下图分别是 7 个这样的任务、8 个这样的任务和 16 个这样的任务的耗时：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-15-15-08-59.png&quot; alt=&quot;简单异步任务的耗时&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以发现，8 个任务和 16 个任务的耗时很不正常。&lt;/p&gt;

&lt;p&gt;在实际的测试当中，1~7 个任务的耗时几乎相同，而到后面每增加一个任务会增加大量时间。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;任务个数&lt;/th&gt;
      &lt;th&gt;耗时 (ms)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;39&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;54&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;58&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;49&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;45&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;54&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;1027&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;9&lt;/td&gt;
      &lt;td&gt;2030&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10&lt;/td&gt;
      &lt;td&gt;3027&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;4027&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;5032&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;13&lt;/td&gt;
      &lt;td&gt;6027&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;14&lt;/td&gt;
      &lt;td&gt;7029&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;15&lt;/td&gt;
      &lt;td&gt;8025&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;16&lt;/td&gt;
      &lt;td&gt;9025&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;任务计时采用的是 Stopwatch，关于为什么要使用这种计时方式，可以阅读 &lt;a href=&quot;/post/dotnet-high-precision-performance-counting&quot;&gt;.NET/C# 在代码中测量代码执行耗时的建议（比较系统性能计数器和系统时间）&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-15-15-17-59.png&quot; alt=&quot;统计图表&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中，我们可以很直观地观察到，每多一个任务，就会多花 1 秒的事件。这可以认为默认情况下线程池在增加线程的时候，发现如果线程不够，会等待 1 秒之后才会创建新的线程。&lt;/p&gt;

&lt;h2 id=&quot;最简复现代码&quot;&gt;最简复现代码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv task demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartNew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;耗时: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;/post/default-task-scheduler-and-thread-pool&quot;&gt;.NET 默认的 TaskScheduler 和线程池（ThreadPool）设置&lt;/a&gt; 了解线程池创建新工作线程的规则。这里其实真的是类似于死锁的一个例子。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一开始，我们创建了 n 个 Task，然后分别安排在线程池中执行，并在每个 Task 中等待任务执行完毕；&lt;/li&gt;
  &lt;li&gt;随后这 n 个 Task 分别再创建了 n 个子 Task，并继续安排在线程池中执行；&lt;/li&gt;
  &lt;li&gt;这时问题来了，由于前面 n 个 Task 在等待中，所以占用了线程池的线程资源：
    &lt;ul&gt;
      &lt;li&gt;如果 n &amp;lt; 线程池最小线程数，那么当前线程池中还有剩余工作线程帮助完成子 Task；&lt;/li&gt;
      &lt;li&gt;但如果 n &amp;gt;= 线程池最小线程数，那么当前线程池中便没有新的工作线程来完成子 Task；于是一开始的等待也不会完成；必须等线程池开启新的工作线程后，任务才可以继续。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;带线程池开启新的线程之前，以上那些线程就是处于死锁的状态！由于线程池开启新的工作线程需要等待一段时间（例如每秒最多开启一个新的线程），所以每增加一个这样的任务，那么消耗的时间便会持续增加。&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;去掉这里本来多余的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 问题便可以解决。或者一直 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 中间不要转换为同步代码，那么问题也能解决。&lt;/p&gt;

&lt;p&gt;我会遇到以上代码，是因为在库中写了类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoAsync&lt;/code&gt; 那样的方法。同时为了方便使用，封装了一个同步等待的属性。在业务使用方，觉得获取此属性可能比较耗时，于是用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 在后台线程调用。同时由于这是一个可能大量并发的操作，于是造成了以上悲剧。&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/task-wait-may-cause-long-time-waiting.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/task-wait-may-cause-long-time-waiting.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁</title>
        <description>&lt;p&gt;我在 &lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock）&lt;/a&gt; 一文中站在类库使用者的角度看 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 代码的死锁问题；而本文将站在类库设计者的角度来看死锁问题。&lt;/p&gt;

&lt;p&gt;阅读本文，我们将知道如何编写类库代码，来尽可能避免类库使用者出现那篇博客中描述的死锁问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;可能死锁的代码&quot;&gt;可能死锁的代码&lt;/h2&gt;

&lt;p&gt;现在，我们是类库设计者的身份，我们试图编写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;RunAsync&lt;/code&gt; 方法用以异步执行某些操作。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 某些异步操作。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;类库的使用者可能多种多样，一个比较有素养的使用者会考虑这样使用类库：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;放心，这样的类库使用者是不会出什么岔子的。&lt;/p&gt;

&lt;p&gt;然而，这世间既然有让人省心的类库使用者，当然也存在非常让人不省心的类库使用者。当你的类库遍布全球，你真的会遇到这样的使用者：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;或者高级一些，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AutoResetEvent&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 块的使用者：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 这段代码如果在 foo.RunAsync() 第一次调用返回之前再调用一次，则可能死锁。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_autoResetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_autoResetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果这段代码在 UI 线程执行，那么极有可能出现死锁，就是我在 &lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock）&lt;/a&gt; 一文中说的那种死锁，详情可进去看原因。&lt;/p&gt;

&lt;p&gt;那么现在做一个调查，你认为下面三种 &lt;code class=&quot;highlighter-rouge&quot;&gt;RunAsync&lt;/code&gt; 的实现中，哪些会在碰到这种不省心的类库使用者时发生死锁呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-23-21-19-51.png&quot; alt=&quot;三种实现&quot; /&gt;&lt;/p&gt;

&lt;p&gt;答案是——&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第 2 种&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;只有第 2 种会发生死锁，第 1 和第 3 种都不会。&lt;/p&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;对于第 2 种情况，下方“&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 之后的代码”试图回到 UI 线程执行，但 UI 此时处于调用者 &lt;code class=&quot;highlighter-rouge&quot;&gt;foo.RunAsync().Wait();&lt;/code&gt; 这段神奇代码的等待状态——所以死锁了。回到 UI 线程靠的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt;，我在 &lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock）&lt;/a&gt; 一文中已有解释，建议前往了解更深层次的原因。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 某些异步操作。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// await 之后的代码（即使没写任何代码，也是需要执行的）。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;那为什么第 1 种和第 3 种不会死锁呢？&lt;/p&gt;

&lt;p&gt;对第 1 种情况，由于并没有写 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;，所以异步状态机 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncMethodStateMachine&lt;/code&gt; 此时并不执行。直接返回了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;，这相当于此时创建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 对象直接被调用者的 &lt;code class=&quot;highlighter-rouge&quot;&gt;foo.RunAsync().Wait();&lt;/code&gt; 神奇代码等待了。也就是说，等待的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 是真正执行异步任务的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt; 方法内部通过自旋锁来实现等待，可以阅读 &lt;a href=&quot;/post/lightweight-thread-safe-since-dotnet-4&quot;&gt;.NET 中的轻量级线程安全 - walterlv&lt;/a&gt; 了解自旋锁，也可以前往 .NET Framework 源码 &lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,b1c8bf867b403050,references&quot;&gt;Task.SpinWait&lt;/a&gt; 了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.SpinWait()&lt;/code&gt; 方法的具体实现。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//spin only once if we are running on a single CPU&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spinCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PlatformHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsSingleProcessor&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpinWait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YIELD_THRESHOLD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spinCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spinCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SpinWait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PlatformHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessorCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 中的异步任务结束后，自旋锁即发现任务结束 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.IsCompleted&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;，于是等待结束，不会发生死锁。&lt;/p&gt;

&lt;p&gt;对第 3 种情况，由于指定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConfigureAwait(false)&lt;/code&gt;，这意味着通知异步状态机 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncMethodStateMachine&lt;/code&gt; 并不需要使用设置好的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;（对于 UI 线程，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt;）执行线程同步，而是使用默认的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;，而默认行为是随便找个线程执行后面的代码。于是，&lt;code class=&quot;highlighter-rouge&quot;&gt;await Task.Run&lt;/code&gt; 后面的代码便不需要返回原线程，也就不会发生第 2 种情况里的死锁问题。&lt;/p&gt;

&lt;h2 id=&quot;预防&quot;&gt;预防&lt;/h2&gt;

&lt;p&gt;建议安装 NuGet 包 &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers/&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt;。这样，当你在代码中写出 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 时，分析器会提示你 &lt;a href=&quot;/post/meaning-of-all-kind-of-stylecop&quot;&gt;CA2007&lt;/a&gt; 警告，你必须显式设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConfigureAwait(false)&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConfigureAwait(true)&lt;/code&gt; 来提醒你是否需要使用默认的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果你是类库的编写者，注意此问题能够一定程度上防止逗比使用者出现死锁问题后喷你的类库写得不好。&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/using-configure-await-to-avoid-deadlocks.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/using-configure-await-to-avoid-deadlocks.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame）</title>
        <description>&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步模型（即 TAP &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?wt.mc_id=MVP&quot;&gt;Task-based Asynchronous Pattern&lt;/a&gt;）出现以前，有大量的同步代码存在于代码库中，以至于这些代码全部迁移到 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 可能有些困难。这里就免不了将一部分异步代码修改为同步代码。然而传统的迁移方式存在或多或少的问题。本文将总结这些传统方法的坑，并推出一款异步转同步的新方法，解决传统方法的这些坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;背景问题和传统方法&quot;&gt;背景问题和传统方法&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;为什么有些方法不容易迁移到 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;？
    &lt;ul&gt;
      &lt;li&gt;参见微软的博客 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 最佳实践 &lt;a href=&quot;https://msdn.microsoft.com/en-us/magazine/jj991977.aspx&quot;&gt;Async/Await - Best Practices in Asynchronous Programming&lt;/a&gt;。如果某个方法从同步方法修改为异步方法（例如从 &lt;code class=&quot;highlighter-rouge&quot;&gt;var content = file.Read()&lt;/code&gt; 修改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;var content = await file.ReadAsync()&lt;/code&gt;），那么调用此方法的整个调用链全部都要改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 才能让返回值在调用链中成功传递。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;传统的异步转同步的方法有哪些？有什么坑？
    &lt;ul&gt;
      &lt;li&gt;参见我的好朋友&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt;的博客 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E5%BC%82%E6%AD%A5%E8%BD%AC%E5%90%8C%E6%AD%A5.html&quot;&gt;win10 uwp 异步转同步&lt;/a&gt;。文章里使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Wait()&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Result&lt;/code&gt; 来获取异步方法的返回值。&lt;/li&gt;
      &lt;li&gt;这种方法会阻塞调用线程。如果调用线程是 UI 线程，那么 UI 将会无响应；更严重地，如果 UI 线程使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt;（参见我的另一篇文章 &lt;a href=&quot;/post/yield-in-task-dispatcher&quot;&gt;DispatcherSynchronizationContext - walterlv&lt;/a&gt;）进行线程上下文的同步，那么极有可能会造成死锁（参见我的另一篇文章 &lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;安全的方法&quot;&gt;安全的方法&lt;/h2&gt;

&lt;p&gt;传统方法的坑在于 UI 线程无响应和死锁问题。既要解决无响应问题，又要阻塞调用方，可选的方法就是 Windows 消息循环了。在使用消息循环时还要避免使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的同步上下文（&lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;），这样才能避免 UI 线程的死锁问题。&lt;/p&gt;

&lt;p&gt;所以，我考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 来阻塞当前线程并创建一个新的消息循环。使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.ContinueWith&lt;/code&gt; 来恢复阻塞，而不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 中默认同步所采用的同步上下文。&lt;/p&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;，可以阅读 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分）&lt;/a&gt; 了解更多。&lt;/p&gt;

&lt;p&gt;代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 通过 PushFrame（进入一个新的消息循环）的方式来同步等待一个必须使用 await 才能等待的异步操作。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 由于使用了消息循环，所以并不会阻塞 UI 线程。&amp;lt;para/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 此方法适用于将一个 async/await 模式的异步代码转换为同步代码。&amp;lt;para/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 此方法适用于任何线程，包括 UI 线程、非 UI 线程、STA 线程、MTA 线程。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;typeparam name=&quot;TResult&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 异步方法返回值的类型。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 我们认为只有包含返回值的方法才会出现无法从异步转为同步的问题，所以必须要求异步方法返回一个值。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/typeparam&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;task&quot;&amp;gt;异步的带有返回值的任务。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;异步方法在同步返回过程中的返回值。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AwaitByPushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContinueWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 这就是全部代码了，仅适用于 Windows 平台（&lt;em&gt;如果使用 .NET Core，需要其他能够创建消息循环这种线程模型的方案。不过这通常是平台相关的，需要多种实现。例如 &lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia&quot;&gt;Avalonia&lt;/a&gt; 在 Win32 平台上使用 GetMessage 实现等待；在 iOS 和 Android 平台上使用外部的全局循环；Mac 使用 MonoMac.AppKit 创建；Linux 下使用 GtkMainIteration 实现等待。&lt;/em&gt;）&lt;/p&gt;

&lt;h2 id=&quot;新方法的适用范围和优劣&quot;&gt;新方法的适用范围和优劣&lt;/h2&gt;

&lt;p&gt;事实上，虽然我们使用了消息循环，但其实也适用于控制台程序，适用于各种各样奇奇怪怪的线程 —— &lt;strong&gt;无论是 UI 线程还是非 UI 线程，无论是 STA 还是 MTA&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;例如，我们现在在一个 MTA 线程模型的控制台程序中试用一下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv's demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AwaitByPushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;输入的字符串为：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;请稍后……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;请输入：&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;正在处理……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 模拟耗时的操作。&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动控制台程序，我们发现程序真的停下来等待我们输入了。这说明一开始的 &lt;code class=&quot;highlighter-rouge&quot;&gt;await Task.Delay(1000)&lt;/code&gt; 已经生效，&lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数也没有退出。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-11-46-02.png&quot; alt=&quot;开始运行&quot; /&gt;&lt;br /&gt;
▲ 开始运行&lt;/p&gt;

&lt;p&gt;现在我们输入一段文字：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-11-47-37.png&quot; alt=&quot;输入文字&quot; /&gt;&lt;br /&gt;
▲ 输入文字&lt;/p&gt;

&lt;p&gt;依然正常。现在我们按下回车看看后台线程的执行是否也正常：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-11-50-23.png&quot; alt=&quot;后台线程正在处理&quot; /&gt;&lt;br /&gt;
▲ 后台线程正在处理&lt;/p&gt;

&lt;p&gt;后台线程也在处理，而且现在才停到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadKey&lt;/code&gt; 中。说明转同步过程成功。&lt;/p&gt;

&lt;p&gt;不过我们也要认识到，由于使用了消息循环，这意味着此方法不像 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Wait()&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Result&lt;/code&gt; 方法那样在全平台通用。不过，消息循环方法的出现便主要是用来解决 UI 的无响应和死锁问题。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;我们使用消息循环的方式完成了异步方法转同步方法，这样的方式不止能解决传统 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Wait()&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Result&lt;/code&gt; 导致 UI 线程无响应或死锁问题之外，也适用于非 UI 线程，不止能在 STA 线程使用，也能在 MTA 线程使用。&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/convert-async-to-sync-by-push-frame.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/convert-async-to-sync-by-push-frame.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 Task.Wait()？立刻死锁（deadlock）</title>
        <description>&lt;p&gt;最近读到一篇异步转同步的文章，发现其中没有考虑到异步转同步过程中发生的死锁问题，所以特地在本文说说异步转同步过程中的死锁问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;文章作者 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 已经修复了描述：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E5%BC%82%E6%AD%A5%E8%BD%AC%E5%90%8C%E6%AD%A5.html&quot;&gt;win10 uwp 异步转同步&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;什么情况下会产生死锁&quot;&gt;什么情况下会产生死锁？&lt;/h2&gt;

&lt;p&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Wait()&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Result&lt;/code&gt; 立刻产生死锁的充分条件：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; 的代码位于 UI 线程；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的实际执行在其他线程，且需要返回 UI 线程。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;死锁的原因：&lt;/p&gt;

&lt;p&gt;UWP、WPF、Windows Forms 程序的 UI 线程都是单线程的。为了让使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的代码像使用同步代码一样简单，WPF 程序的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 类在构造的时候会将主 UI 线程 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的同步上下文设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt; 的实例，这在我的另一篇文章 &lt;a href=&quot;/post/yield-in-task-dispatcher.html#taskyield&quot;&gt;Task.Yield&lt;/a&gt; 中也有过说明。&lt;/p&gt;

&lt;p&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的任务结束时，会从 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncMethodStateMachine&lt;/code&gt; 中调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnComplete()&lt;/code&gt; 方法，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 后续方法的执行靠的就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnComplete()&lt;/code&gt; 方法中一层层调用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt; 里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Post&lt;/code&gt; 方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Asynchronously invoke the callback in the SynchronizationContext.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendOrPostCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Call BeginInvoke with the cached priority.  Note that BeginInvoke&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// preserves the behavior of passing exceptions to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Dispatcher.UnhandledException unlike InvokeAsync.  This is&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// desireable because there is no way to await the call to Post, so&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// exceptions are hard to observe.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里就是问题的关键！！！&lt;/p&gt;

&lt;p&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher.BeginInvoke(_priority, d, state);&lt;/code&gt; 这句代码在后台线程，那么此时 UI 线程处于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; 调用中的阻塞状态，&lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt; 中的任务是无论如何也无法执行到的！于是无论如何都无法完成这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Post&lt;/code&gt; 任务，即无论如何也无法退出此异步任务的执行，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt; 便无法完成等待……死锁……&lt;/p&gt;

&lt;p&gt;这里给出最简复现的例子代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;无论是 WPF 还是 UWP，只要在 UI 线程上调用上述代码，必然死锁！&lt;/p&gt;

&lt;h2 id=&quot;什么情况下不会产生死锁&quot;&gt;什么情况下不会产生死锁？&lt;/h2&gt;

&lt;p&gt;阅读了本文一开始说的那篇文章 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E5%BC%82%E6%AD%A5%E8%BD%AC%E5%90%8C%E6%AD%A5.html&quot;&gt;win10 uwp 异步转同步&lt;/a&gt; 后，你一定好奇为什么此文的情况不会产生死锁。&lt;/p&gt;

&lt;p&gt;那是因为，它不满足本文提到的充分条件——&lt;code class=&quot;highlighter-rouge&quot;&gt;StorageFolder.GetFolderFromPathAsync(&quot;&quot;)&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;StorageFolder.GetFolderFromPathAsync(&quot;&quot;)&lt;/code&gt; 这两个方法&lt;strong&gt;并不会在后台线程执行&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;逗我？？？不在后台线程执行怎么做到的异步等待！！！&lt;/p&gt;

&lt;p&gt;是的，读写文件，访问网络，这些 IO 阻塞的操作执行时，里面&lt;strong&gt;根本就没有线程&lt;/strong&gt;，详情请阅读：&lt;a href=&quot;http://blog.stephencleary.com/2013/11/there-is-no-thread.html&quot;&gt;There Is No Thread&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;还有另一些操作，也没有后台线程的参与，于是也不存在从后台线程回到主线程导致死锁的情况。如 &lt;a href=&quot;/post/yield-in-task-dispatcher.html#taskyield&quot;&gt;Task.Yield&lt;/a&gt;，还有 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;InvokeAsync&lt;/a&gt;，它们也不会造成死锁。&lt;/p&gt;

&lt;p&gt;另外，如果是控制台程序，或者一个普通的非 UI 线程，其 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 为 null，那么异步任务执行完后不需要回到原有线程，也不会造成死锁。&lt;/p&gt;

&lt;p&gt;总结不会造成死锁的充分条件：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;异步操作执行完后不需要回到原有线程（例如非 UI 线程和控制台线程）；&lt;/li&gt;
  &lt;li&gt;异步操作不需要单独的线程执行任务。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;如何避免死锁&quot;&gt;如何避免死锁？&lt;/h2&gt;

&lt;p&gt;明确了会造成死锁的条件和不会造成死锁的条件后，我们只需要做到以下几点即可避免死锁了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 UI 线程，如果使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;，就尽量不要再使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Wait()&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Result&lt;/code&gt; 了，就一直异步一条路走到黑好了（微软称其为 Async All the Way）。&lt;/li&gt;
  &lt;li&gt;如果可能，尽量在异步任务后添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;.ConfigureAwait(false)&lt;/code&gt;；这样，异步任务后面继续执行的代码就不会回到原 UI 线程了，而是直接从线程池中再取出一个线程执行；这样，即便 UI 线程后续可能有别的原因造成阻塞，也不会产生死锁了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;把原来的代码改成这样，就不会死锁了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没错！只能是一路 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;。微软将其描述为：&lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 会像病毒一样在你的代码中传播。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Others have also noticed the spreading behavior of asynchronous programming and have called it “contagious” or compared it to a zombie virus.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这句话的原文参见：&lt;a href=&quot;https://msdn.microsoft.com/en-us/magazine/jj991977.aspx&quot;&gt;Async/Await - Best Practices in Asynchronous Programming&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;为了防止真的有代码的调用者使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt;，我们也得写出防 SB 的代码来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这一句的目的是防止执行上下文切换回 UI 线程。&lt;/p&gt;

&lt;p&gt;这样，即便真的使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoAsync().Wait()&lt;/code&gt; 也不会发生死锁。注意，&lt;strong&gt;整个方法调用链都需要使用&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;.ConfigureAwait(false)&lt;/code&gt; &lt;strong&gt;才能够防止线程切换时，在调用方的&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt; &lt;strong&gt;方法中发生死锁&lt;/strong&gt;。详见我的另一篇博客 &lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁&lt;/a&gt;。）&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.stephencleary.com/2013/11/there-is-no-thread.html&quot;&gt;There Is No Thread&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/magazine/jj991977.aspx&quot;&gt;Async/Await - Best Practices in Asynchronous Programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deadlock-in-task-wait.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deadlock-in-task-wait.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 Lazy 中导致死锁</title>
        <description>&lt;p&gt;WPF 中为了 UI 的跨线程访问，提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 线程模型。其 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 方法，无论在哪个线程调用，都可以让传入的方法回到 UI 线程。&lt;/p&gt;

&lt;p&gt;然而，如果你在 Lazy&lt;T&gt; 上下文中使用了 `Invoke`，那么当这个 `Lazy&lt;T&gt;` 跨线程并发时，极有可能导致死锁。本文将具体说说这个例子。&lt;/T&gt;&lt;/T&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一段死锁的代码&quot;&gt;一段死锁的代码&lt;/h2&gt;

&lt;p&gt;请先看一段非常简单的 WPF 代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_walterlvLazy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在后台线程通过 Lazy 获取。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backgroundWalterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_walterlvLazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 等待一个时间，这样可以确保后台线程先访问到 Lazy，并且在完成之前，UI 线程也能访问到 Lazy。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 在主线程通过 Lazy 获取。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_walterlvLazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv&lt;/code&gt; 类的定义也是非常简单的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 等待一段时间，是为了给我么的测试程序一个准确的时机。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Invoke 到主线程执行，里面什么都不做是为了证明绝不是里面代码带来的影响。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current.Dispatcher&lt;/code&gt; 并不一定必须是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Current&lt;/code&gt;，只要是两个不同线程拿到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的实例是同一个，就会死锁。&lt;/p&gt;

&lt;h2 id=&quot;此死锁的触发条件&quot;&gt;此死锁的触发条件&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 的线程安全参数设置为默认的，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;LazyThreadSafetyMode.ExecutionAndPublication&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;后台线程和主 UI 线程并发访问这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;，且后台线程先于主 UI 线程访问这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 内部的代码包含主线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;此死锁的原因&quot;&gt;此死锁的原因&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;后台线程访问到 Lazy，于是 Lazy 内部获得同步锁；&lt;/li&gt;
  &lt;li&gt;主 UI 线程访问到 Lazy，于是主 UI 线程等待同步锁完成，并进入阻塞状态（以至于不能处理消息循环）；&lt;/li&gt;
  &lt;li&gt;后台线程的初始化调用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 需要到 UI 线程完成指定的任务后才会返回，但 UI 线程此时阻塞不能处理消息循环，以至于无法完成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 内的任务；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是，后台线程在等待 UI 线程处理消息以便让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 完成，而主 UI 线程由于进入 Lazy 的等待，于是不能完成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 中的任务；于是发生死锁。&lt;/p&gt;

&lt;h2 id=&quot;此死锁的解决方法&quot;&gt;此死锁的解决方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 便能解锁。&lt;/p&gt;

&lt;p&gt;这么做能解决的原因是：后台线程能够及时返回，这样 UI 线程便能够继续执行，包括执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 中传入的任务。&lt;/p&gt;

&lt;p&gt;实际上，以上可能是最好的解决办法了。因为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们使用 Lazy 并且设置线程安全，一定是因为这个初始化过程会被多个线程访问；&lt;/li&gt;
  &lt;li&gt;我们会在 Lazy 的初始化代码中使用回到主线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt;，也是因为我们预料到这份初始化代码可能在后台线程执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，这段初始化代码既然不可避免地会并发，那么就应该阻止并发造成的死锁问题。也就是不要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 而是改用 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 的返回值，那么改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 之后，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步等待返回值。&lt;/p&gt;

&lt;h2 id=&quot;更多死锁问题&quot;&gt;更多死锁问题&lt;/h2&gt;

&lt;p&gt;死锁问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-in-task-wait&quot;&gt;使用 Task.Wait()？立刻死锁（deadlock） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-of-invoke-in-lazy&quot;&gt;不要使用 Dispatcher.Invoke，因为它可能在你的延迟初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 中导致死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/task-wait-may-cause-long-time-waiting&quot;&gt;.NET 中小心嵌套等待的 Task，它可能会耗尽你线程池的现有资源，出现类似死锁的情况 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-async-to-sync-by-push-frame&quot;&gt;将 async/await 异步代码转换为安全的不会死锁的同步代码（使用 PushFrame） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Mar 2019 07:52:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deadlock-of-invoke-in-lazy.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deadlock-of-invoke-in-lazy.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>C#/.NET 如何结束掉一个进程</title>
        <description>&lt;p&gt;本文介绍如何结束掉一个进程。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;结束掉特定名字的进程&quot;&gt;结束掉特定名字的进程&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessInfo&lt;/code&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Kill&lt;/code&gt; 实例方法可以调用，也就是说如果我们能够拿到一个进程的信息，并且对这个进程拥有访问权限，那么我们就能够结束掉它。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetProcessesByName(processName)&lt;/code&gt; 可以按照名字拿到进程信息。于是我们可以使用这个方法杀掉具有特定名称的进程。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;KillProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProcessesByName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 杀掉这个进程。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Kill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 等待进程被杀掉。你也可以在这里加上一个超时时间（毫秒整数）。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitForExit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Win32Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 无法结束进程，可能有很多原因。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 建议记录这个异常，如果你的程序能够处理这里的某种特定异常了，那么就需要在这里补充处理。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Log.Error(ex);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 进程已经退出，无法继续退出。既然已经退了，那这里也算是退出成功了。&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 于是这里其实什么代码也不需要执行。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;结束掉自己&quot;&gt;结束掉自己&lt;/h2&gt;

&lt;p&gt;可以是参见林德熙的博客，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.FailFast&lt;/code&gt;，在结束掉自己的时候记录自己的错误日志。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-%E4%BD%BF%E7%94%A8-environment.failfast-%E7%BB%93%E6%9D%9F%E7%A8%8B%E5%BA%8F&quot;&gt;dotnet 使用 Environment.FailFast 结束程序 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 14:57:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-kill-a-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-kill-a-process.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>让你的 VSCode 具备调试 C# 语言 .NET Core 程序的能力</title>
        <description>&lt;p&gt;如果你是开发个人项目，那就直接用 Visual Studio Community 版本吧，对个人免费，对小团体免费，不需要这么折腾。&lt;/p&gt;

&lt;p&gt;如果你是 Mac / Linux 用户，不想用 Visual Studio for Mac 版；或者不想用 Visual Studio for Windows 版那么重磅的 IDE 来开发简单的 .NET Core 程序；或者你就是想像我这么折腾，那我们就开始吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-net-core-sdkvisual-studio-code-和-c-for-visual-studio-code&quot;&gt;安装 .NET Core Sdk、Visual Studio Code 和 C# for Visual Studio Code&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;点击这里下载正式或者预览版的 .NET Core&lt;/a&gt; 然后安装&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/download&quot;&gt;点击这里下载 Visual Studio Code&lt;/a&gt; 然后安装&lt;/li&gt;
  &lt;li&gt;在 Visual Studio Code 里安装 C# for Visual Studio Code 插件（步骤如下图所示）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-01-52.png&quot; alt=&quot;安装 C# for Visual Studio Code 插件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;搜索的时候，推荐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;OmniSharp&lt;/code&gt; 关键字，因为这可以得到唯一的结果，你不会弄混淆。&lt;em&gt;如果你使用 C# 作为关键字，那需要小心，你得找到名字只有 C#，点开之后是 C# for Visual Studio Code 的那款插件。因为可能装错，所以我不推荐这么做。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;对于新版的 Visual Studio Code，装完会自动启用，所以你不用担心。我们可以后续步骤了。&lt;/p&gt;

&lt;h2 id=&quot;使用-vscode-创建-net-core-项目&quot;&gt;使用 VSCode 创建 .NET Core 项目&lt;/h2&gt;

&lt;p&gt;本文不会讲解如何使用 VSCode 创建 .NET Core 项目，因为这不是本文的重点。&lt;/p&gt;

&lt;p&gt;也许你可以参考我还没有写的另一篇博客。&lt;/p&gt;

&lt;h2 id=&quot;打开一个现有的-net-core-项目&quot;&gt;打开一个现有的 .NET Core 项目&lt;/h2&gt;

&lt;p&gt;现在假设你已经有一个现成的能用 Visual Studio 跑起来的 .NET Core 控制台项目了（可能是刚克隆下来的，也可能就是用我另一篇博客中的教程创建的），于是我们就在这个项目上进行开发。&lt;/p&gt;

&lt;p&gt;本文以我的自动化测试程序 Walterlv.InfinityStartupTest 为例进行说明。如果你找不到合适的例子，可以使用这篇博客创建一个。&lt;/p&gt;

&lt;p&gt;在这个文件夹的根目录下右键，然后 &lt;code class=&quot;highlighter-rouge&quot;&gt;使用 Code 打开&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-14-15.png&quot; alt=&quot;使用 Visual Studio Code 打开文件夹&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;配置编译和调试环境&quot;&gt;配置编译和调试环境&lt;/h2&gt;

&lt;p&gt;正常情况下，当你用 Visual Studio Code 打开一个包含 .NET Core 项目的文件夹时，C# 插件会在右下角弹出通知提示，问你要不要为这个项目创建编译和调试文件，当然选择“Yes”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-23-09.png&quot; alt=&quot;创建编译和调试文件的提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个提示一段时间不点会消失的，但是右下角会有一个小铃铛（上面的图片也可以看得到的），点开可以看到刚刚消失的提示，然后继续操作。&lt;/p&gt;

&lt;p&gt;这时，你的项目文件夹中会多出两个文件，都在 .vscode 文件夹中。&lt;code class=&quot;highlighter-rouge&quot;&gt;tasks.json&lt;/code&gt; 是编译文件，指导如何进行编译；&lt;code class=&quot;highlighter-rouge&quot;&gt;launch.json&lt;/code&gt; 是调试文件，指导如何进行调试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-39-17.png&quot; alt=&quot;多出的编译文件和调试文件&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;开始调试&quot;&gt;开始调试&lt;/h2&gt;

&lt;p&gt;现在，你只需要按下 F5（就是平时 Visual Studio 调试按烂的那个），你就能使用熟悉的调试方式在 Visual Studio Code 中来调试 .NET Core 程序了。&lt;/p&gt;

&lt;p&gt;下图是调试进行中各个界面的功能分区。如果你没看到这个界面，请点击左侧那只被圈在圆圈里面的小虫子。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-20-52-08.png&quot; alt=&quot;Visual Studio Code 中的 .NET Core 调试界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当你按照本文操作，在按下 F5 后有各种报错，那么原因只有一个——你的这个项目本身就是编译不过的，你自己用命令行也会编译不过。你需要解决编译问题，而本文只是入门教程，不会说如何解决编译问题。&lt;/p&gt;

&lt;h2 id=&quot;手工设置-tasksjson-和-launchjson-文件&quot;&gt;手工设置 tasks.json 和 launch.json 文件&lt;/h2&gt;

&lt;p&gt;如果自动创建的这两个文件有问题，或者你根本就找不到自动创建的入口，可以考虑手工创建这两个文件。&lt;/p&gt;

&lt;p&gt;请参见博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/equip-vscode-manually-for-dotnet-core-app-debugging&quot;&gt;手工编辑 tasks.json 和 launch.json，让你的 VSCode 具备调试 .NET Core 程序的能力&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;还补充一句，本文说编译文件和调试文件是不对的，因为在 Visual Studio Code 中没有编译这个概念，编译只是任务中的一种而已。&lt;/p&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 14:31:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/equip-vscode-for-dotnet-core-app-debugging.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/equip-vscode-for-dotnet-core-app-debugging.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>vscode</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>手工编辑 tasks.json 和 launch.json，让你的 VSCode 具备调试 .NET Core 程序的能力</title>
        <description>&lt;p&gt;如果 C# for Visual Studio Code 没有办法自动为你生成正确的 tasks.json 和 launch.json 文件，那么可以考虑阅读本文手工创建他们。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;前期准备&quot;&gt;前期准备&lt;/h2&gt;

&lt;p&gt;你需要安装 .NET Core Sdk、Visual Studio Code 和 C# for Visual Studio Code，然后打开一个 .NET Core 的项目。如果你没有准备，请先阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/equip-vscode-for-dotnet-core-app-debugging&quot;&gt;让你的 VSCode 具备调试 C# 语言 .NET Core 程序的能力&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文主要处理自动生成的配置文件无法满足要求，手工生成。&lt;/p&gt;

&lt;h2 id=&quot;半自动创建-tasksjson-和-launchjson&quot;&gt;半自动创建 tasks.json 和 launch.json&lt;/h2&gt;

&lt;p&gt;这依然是个偷懒的好方案，我喜欢。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;按下 F5；&lt;/li&gt;
  &lt;li&gt;在弹出的列表中，选择 .NET Core；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-22-14-37.png&quot; alt=&quot;选择 .NET Core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-14-22-15-53.png&quot; alt=&quot;自动生成的 tasks.json 和 launch.json&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你不需要再做什么其他的工作了，这时再按下 F5 你已经可以开始调试了。&lt;/p&gt;

&lt;h2 id=&quot;全手工创建-tasksjson-和-launchjson&quot;&gt;全手工创建 tasks.json 和 launch.json&lt;/h2&gt;

&lt;p&gt;tasks.json 定义一组任务。其中我们需要的是编译任务，通常编译一个项目使用的动词是 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt;。比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令就是这样的动词。&lt;/p&gt;

&lt;p&gt;于是定义一个名字为 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 的任务，对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;label&lt;/code&gt; 标签。&lt;code class=&quot;highlighter-rouge&quot;&gt;command&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt; 对应我们在命令行中编译一个项目时使用的命令行和参数。&lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;process&lt;/code&gt; 表示此任务是启动一个进程。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tasks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dotnet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;process&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceFolder}/Walterlv.InfinityStartupTest/Walterlv.InfinityStartupTest.csproj&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;problemMatcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$msCompile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 launch.json 中通常配置两个启动配置，一个是启动调试，一个是附加调试。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 是在安装了 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp&quot;&gt;C# for Visual Studio Code (powered by OmniSharp)&lt;/a&gt; 插件之后才会有的调试类型。&lt;code class=&quot;highlighter-rouge&quot;&gt;preLaunchTask&lt;/code&gt; 表示在此启动开始之前需要执行的任务，这里指定的 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 跟前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; 任务就关联起来了。&lt;code class=&quot;highlighter-rouge&quot;&gt;program&lt;/code&gt; 是调试的程序路径，&lt;code class=&quot;highlighter-rouge&quot;&gt;console&lt;/code&gt; 指定调试控制台使用内部控制台。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;configurations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;调试 Walterlv 的自动化测试程序&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;coreclr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;request&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;launch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;preLaunchTask&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;program&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceFolder}/Walterlv.InfinityStartupTest/bin/Debug/netcoreapp3.0/Walterlv.InfinityStartupTest.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cwd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceFolder}/Walterlv.InfinityStartupTest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;console&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;internalConsole&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;stopAtEntry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;internalConsoleOptions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;openOnSessionStart&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;附加进程&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;coreclr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;request&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;attach&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;processId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${command:pickProcess}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样自己手写的方式更灵活但是也更难。&lt;/p&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 14:31:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/equip-vscode-manually-for-dotnet-core-app-debugging.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/equip-vscode-manually-for-dotnet-core-app-debugging.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>vscode</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>自然码的形码</title>
        <description>&lt;p&gt;使用拼音/双拼输入法，如果你的打字速度还需要继续提升，那么就不应该再不断地看着候选框打字了。使用双拼形码可以规避相当多字词的选字。&lt;/p&gt;

&lt;p&gt;本文整理自然码的形码，然后附带一张我自己制作的自然码形码的键盘图。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;输入法的选择&quot;&gt;输入法的选择&lt;/h2&gt;

&lt;p&gt;目前各种双拼输入法中，辅码最接近自然码形码的，是手心输入法。所以我选用了手心输入法并重新训练了词库。&lt;/p&gt;

&lt;p&gt;这款输入法与 360 有点关系，不过很不 360。这让我有点犹豫，不过中文输入法里面也没有几个让人省心得了。小狼毫实在是没法儿折腾到好用呀。&lt;/p&gt;

&lt;h2 id=&quot;自然码的形码&quot;&gt;自然码的形码&lt;/h2&gt;

&lt;p&gt;自然码的形码主要是部首的声母。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; 一 丨 亅 レ 乛 フ ㄥ&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; 八 丷 卜 冖 宀 匕 比 白 贝 疒 鼻&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C&lt;/code&gt; 艹 卄 廾  廿 屮 卝 寸&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D&lt;/code&gt; 丶 冫 氵 刀 刂 リ ㄍ ⺈ 丁 歹 癶&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;E&lt;/code&gt; 二 儿 阝 耳 卩 &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 扌 丰 反 方 风 父 缶 巿&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;G&lt;/code&gt; 乚  ㄅ ㄋ 勹 弓 工 广 艮 戈 瓜 谷 革 骨 鬼 夬 罓&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; 灬 火 禾 户 虍 黑 乊 厷&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;I&lt;/code&gt; 厂 川 巛 亍 车 虫 臣 辰 赤 齿 髟 豖&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;J&lt;/code&gt; 几 九 己 巾 斤 钅 金 见 臼  角&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;K&lt;/code&gt; コ 凵 匚 冂 口 囗 丂&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; 力 六 立 龙 耒 卤 鹿&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;M&lt;/code&gt; 木 门 毛 马 米 矛 母 皿 尨 麻 丏&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;N&lt;/code&gt; 女 牛 牜 ⺧ 鸟&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;O&lt;/code&gt; 日 曰 月 目&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;P&lt;/code&gt; ノ 彡 片 皮 疋 ⺪ 攴&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Q&lt;/code&gt; 七 犭 犬 丌 欠 气 且&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;R&lt;/code&gt; 亻 人 入 肉&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;S&lt;/code&gt; 三 罒 巳 纟 糹 糸 厶 &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 土 田&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;U&lt;/code&gt; 水 手  食 飠 饣 示 礻 山 石 尸 十 士 矢 殳 舌 身 豕 鼠&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;V&lt;/code&gt; 隹 ⺮ 爫 爪 豸 止 至 舟&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;W&lt;/code&gt; 文 亠 攵 夂 夊 ㄨ 王 韦 瓦&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;X&lt;/code&gt; 彳 小  心 忄  血 彐 夕 习 西 辛&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Y&lt;/code&gt; 乙 又 已 讠 言 幺 尤 尢 冘 衣 衤 羊 牙 业 由 用 页 酉 鱼 雨 羽 聿 乑 乂&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Z&lt;/code&gt; 辶 廴 子 自 走 足 ⻊ 卆&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;键盘图&quot;&gt;键盘图&lt;/h2&gt;

&lt;p&gt;将以上的形码整理成一个键盘图，有助于你在练习形码的初期记忆这些形码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-05-17-15-12.png&quot; alt=&quot;自然码形码的键盘图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中所用的背景源自微软流畅设计 Fluent Design System 的新版本，Microsoft Office 新图标设计的视频片段。我自己进行了一些高斯模糊处理。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考链接&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.liuchuo.net/archives/2847&quot;&gt;【双拼输入法】自然码辅助码入门教程（辅助码表） – 柳婼 の blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/24722335&quot;&gt;如何评价手心输入法？ - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 05:01:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/sound-shape-of-natural-code.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/sound-shape-of-natural-code.html</guid>
        
        
        <category>ime</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio Code 中添加自定义的代码片段</title>
        <description>&lt;p&gt;无论是那个编辑器，如果能够添加一些自定义代码片段，能够大大提升代码的输入效率。&lt;/p&gt;

&lt;p&gt;本文介绍如何在 Visual Studio Code 中添加自定义代码片段。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;visual-studio-code-的代码片段设置&quot;&gt;Visual Studio Code 的代码片段设置&lt;/h2&gt;

&lt;p&gt;你可以在 Visual Studio Code 的菜单中找到代码片段的设置入口，在 File -&amp;gt; Preferences -&amp;gt; User Snippets 中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-19-20-57-16.png&quot; alt=&quot;打开用户代码片段设置&quot; /&gt;&lt;br /&gt;
▲ 打开用户代码片段设置&lt;/p&gt;

&lt;p&gt;点开后，会让你选择做什么：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;新建全局代码片段&lt;/li&gt;
  &lt;li&gt;新建适用于当前工作区的代码片段&lt;/li&gt;
  &lt;li&gt;新建特定于语言的全局代码片段&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-19-21-03-44.png&quot; alt=&quot;选择代码片段范围&quot; /&gt;&lt;/p&gt;

&lt;p&gt;根据你的需要选择一个范围：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;比如你需要在任何文件中都能够使用的代码片段，那么选择全局代码片段。&lt;/li&gt;
  &lt;li&gt;比如你需要仅在当前工作区生效的代码片段（&lt;em&gt;例如我写博客是才会用到的博客片段&lt;/em&gt;），那么选择工作区代码片段。&lt;/li&gt;
  &lt;li&gt;如果是特定于语言的，那么选择自己需要的语言。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在新建全局代码片段和当前工作区的代码片段的时候，是需要自己指定名称的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-19-21-11-08.png&quot; alt=&quot;指定代码片段的名称&quot; /&gt;&lt;br /&gt;
▲ 指定代码片段的名称&lt;/p&gt;

&lt;h2 id=&quot;编写代码片段&quot;&gt;编写代码片段&lt;/h2&gt;

&lt;p&gt;无论你使用哪种方式新建代码片段，Visual Studio Code 都会帮你打开这个代码片段文件。整个文件一开始是被注释的状态，就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Place&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;here.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;under&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;description.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;comma&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;separated&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;languages&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;applicable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;field.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;If&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;omitted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;gets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;applied&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;languages.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;what&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;used&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;will&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;be&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;expanded&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;inserted.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Possible&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;are:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;stops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:another&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;placeholders.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Placeholders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;are&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;connected.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Example:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Print to console&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;javascript,typescript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 		&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;console.log('$1');&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 		&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$2&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Log output to console&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的注释，翻译一下是这样的：&lt;/p&gt;

&lt;p&gt;可以将你的全局代码片段放到这里。每一个代码片段都由一个名称来定义，其值包含此代码片段的作用域(scope)、前缀(prefix)、代码片段的内容(body)与其描述信息(description)组成。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;scope 字段中填写以逗号分隔的作用域 Id，如果 scope 字段为空或根本没有设置，那么将适用于所有语言。&lt;/li&gt;
  &lt;li&gt;prefix 是用于触发代码片段的一段文字，当你输入这个文字的时候，你将可以展开这个代码片段的内容并将其插入。&lt;/li&gt;
  &lt;li&gt;body 你可以使用 $1 $2 来作为按下 Tab 时将切换的键盘焦点区域，$0 是插入完成后最终光标所在的位置。你也可以使用 ${1:label} 或 ${2:another} 这样的方式来增加占位符，同样 Id 的占位符将会自动关联起来。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;例如，我通过以下代码片段来为我插入博客的目录：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Add toc to post&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;markdown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;toc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@[TOC](${1:walterlv 的目录})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$0&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;添加 walterlv 的博客的目录&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;插入代码片段&quot;&gt;插入代码片段&lt;/h2&gt;

&lt;p&gt;那么现在按下 F1 打开快捷命令输入框进入 Insert Snippet 命令，输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;toc&lt;/code&gt; 可以看到我们刚刚加入的代码片段：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-01-22.png&quot; alt=&quot;插入代码片段&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-10-05.png&quot; alt=&quot;toc 片段&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或者，在带有智能感知提示的文件中，可以直接通过智能感知提示插入：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-12-39.png&quot; alt=&quot;通过智能感知提示插入&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在插入的代码片段中，&lt;code class=&quot;highlighter-rouge&quot;&gt;${1:walterlv 的目录}&lt;/code&gt; 会成为我们的第一个占位符，而且默认文字就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv 的目录&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-03-55.png&quot; alt=&quot;占位符效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;需要注意的是，Visual Studio Code 中 Markdown 默认是没有打开智能感知提示的。你需要在你的工作区或者全局打开它。&lt;/p&gt;

&lt;p&gt;默认是这样的：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;editor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;be&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;overridden&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;language.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;[markdown]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.wordWrap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.quickSuggestions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你需要把 &lt;code class=&quot;highlighter-rouge&quot;&gt;editor.quickSuggestions&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;[markdown]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.quickSuggestions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个更复杂的例子&quot;&gt;一个更复杂的例子&lt;/h2&gt;

&lt;p&gt;现在，我们来做一个更复杂的例子，以便了解 Visual Studio Code 中代码片段定义的更多内容。&lt;/p&gt;

&lt;p&gt;输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;post&lt;/code&gt; 以便插入 &lt;a href=&quot;/&quot;&gt;blog.walterlv.com&lt;/a&gt; 专用的博客模板：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-34-19.png&quot; alt=&quot;插入博客&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在模板中，我们的的第一个焦点文字是标题，于是我们可以立刻输入博客标题：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-54-35.png&quot; alt=&quot;博客标题占位符&quot; /&gt;&lt;br /&gt;
▲ 博客标题占位符&lt;/p&gt;

&lt;p&gt;当写完后按下 Tab 换到下一个占位符时，可以选择一些常用的选项：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-56-11.png&quot; alt=&quot;选择博客分类&quot; /&gt;&lt;br /&gt;
▲ 选择博客分类&lt;/p&gt;

&lt;p&gt;而最后，焦点会落到博客摘要处：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-08-56-55.png&quot; alt=&quot;最后的焦点在博客摘要&quot; /&gt;&lt;br /&gt;
▲ 最后的焦点在博客摘要&lt;/p&gt;

&lt;p&gt;顺便的，你可能没有注意到还有博客时间。就是那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;date&lt;/code&gt; 字段为空或根本没有设置，那么将适用于所有语言。&lt;/p&gt;

&lt;p&gt;是的 &lt;strong&gt;代码片段中可以插入时间&lt;/strong&gt; 和其他各种变量。&lt;/p&gt;

&lt;p&gt;而这样的一个模板，配置文件是这样的：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Insert a post for blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;markdown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;---&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;title: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;${1:在此处添加标题}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND} +0800&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;categories: ${2|dotnet,csharp,uwp|}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;---&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${0:在此处编辑 blog.walterlv.com 的博客摘要}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;---&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@[TOC](本文内容)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;## 标题&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;---&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**参考资料**&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;使用 blog.walterlv.com 专用的博客模板&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来我们就来说说这是怎么做出来的。&lt;/p&gt;

&lt;h2 id=&quot;关于代码片段编写的更多细节&quot;&gt;关于代码片段编写的更多细节&lt;/h2&gt;

&lt;h3 id=&quot;关于文件名称&quot;&gt;关于文件名称&lt;/h3&gt;

&lt;p&gt;在阅读前面的博客内容时，你可能注意到了：添加全局代码片段的时候，文件扩展名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;.code-snippets&lt;/code&gt;，例如 blog.code-snippets；添加语言特定的代码片段的时候，扩展名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;.json&lt;/code&gt;，如 markdown.json。这个规则无论在全局还是在工作区，都是一样适用的。&lt;/p&gt;

&lt;h3 id=&quot;光标停留点tabstop&quot;&gt;光标停留点（Tabstop）&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$1&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;$2&lt;/code&gt; 这些可以作为按下 Tab 键时的光标停留位置，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;$0&lt;/code&gt; 无论出现在代码片段的哪个地方，都会是最后一个光标位置。&lt;/p&gt;

&lt;h3 id=&quot;占位符&quot;&gt;占位符&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;${1:占位符 Id}&lt;/code&gt; 可以表示一个占位符。当你插入此代码片段的时候，会出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;占位符 Id&lt;/code&gt; 字样，然后光标会选中这几个字以便你进行修改。&lt;/p&gt;

&lt;p&gt;占位符可以嵌套，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;${1:walterlv 的 ${2:嵌套占位符}}&lt;/code&gt;。这时，光标会首先选中所有的文字，随后按下 Tab 之后选中后面那一部分。&lt;/p&gt;

&lt;p&gt;在前面那个比较复杂的博客代码片段中，&lt;code class=&quot;highlighter-rouge&quot;&gt;${1:在此处添加标题}&lt;/code&gt; 就是一个占位符，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;${0:在此处编辑 blog.walterlv.com 的博客摘要}&lt;/code&gt; 就是光标的最终停留点。&lt;/p&gt;

&lt;h3 id=&quot;下拉选项&quot;&gt;下拉选项&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;${1|选项 1,选项 2,选项 3|}&lt;/code&gt; 可以创建三个选项的下拉框。&lt;/p&gt;

&lt;p&gt;在前面的博客代码片段中，&lt;code class=&quot;highlighter-rouge&quot;&gt;${2|dotnet,csharp,uwp|}&lt;/code&gt; 就是一个下拉选框，帮助我选择常用的一些博客类别。&lt;/p&gt;

&lt;h3 id=&quot;变量&quot;&gt;变量&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$变量名&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;${变量名:变量的默认值}&lt;/code&gt; 可以创建变量。&lt;/p&gt;

&lt;p&gt;在 Visual Studio Code 中，你有这些变量可以使用：&lt;/p&gt;

&lt;p&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_SELECTED_TEXT&lt;/code&gt;
	- 在插入代码片段的时刻选中的文本
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_CURRENT_LINE&lt;/code&gt;
	- 在插入代码片段的时刻光标所在的行
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_CURRENT_WORD&lt;/code&gt;
	- 在插入代码片段的时刻光标所在的词
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_LINE_INDEX&lt;/code&gt;
	- 在插入代码片段的时刻的行号（0 为首行）
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_LINE_NUMBER&lt;/code&gt;
	- 当前文档的总行数
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_FILENAME&lt;/code&gt;
	- 当前文档的文件名称
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_FILENAME_BASE&lt;/code&gt;
	- 当前文档不含扩展名的名称
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_DIRECTORY&lt;/code&gt;
	- 当前文档所在的文件夹
-&lt;code class=&quot;highlighter-rouge&quot;&gt;TM_FILEPATH&lt;/code&gt;
	- 当前文档的完全路径
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CLIPBOARD&lt;/code&gt;
	- 剪贴板中的内容
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_YEAR&lt;/code&gt;
	- 年
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_YEAR_SHORT&lt;/code&gt;
	- 两位数字显示的年
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_MONTH&lt;/code&gt;
	- 月，如 02
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_MONTH_NAME&lt;/code&gt;
	- 月的英文名称，如 July
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_MONTH_NAME_SHORT&lt;/code&gt;
	- 月的英文缩写，如 Jul
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_DATE&lt;/code&gt;
	- 日
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_DAY_NAME&lt;/code&gt;
	- 星期的英文名称，如 Monday
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_DAY_NAME_SHORT&lt;/code&gt;
	- 星期的英文缩写，如 Mon
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_HOUR&lt;/code&gt;
	- 24 小时制的时
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_MINUTE&lt;/code&gt;
	- 分
-&lt;code class=&quot;highlighter-rouge&quot;&gt;CURRENT_SECOND&lt;/code&gt;
	- 秒&lt;/p&gt;

&lt;p&gt;所以在上面比较复杂的博客模板中，我们可以直接插入当前的时间 &lt;code class=&quot;highlighter-rouge&quot;&gt;${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND} +0800&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这个时间我之前也在输入法中调过：&lt;a href=&quot;/post/ime-date-time-format&quot;&gt;常用输入法快速输入自定义格式的时间和日期（搜狗/QQ/微软拼音）&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets&quot;&gt;Creating your own snippets in Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 05:01:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-custom-code-snippet-for-vscode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-custom-code-snippet-for-vscode.html</guid>
        
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>解决 mklink 使用中的各种坑（硬链接，软链接/符号链接，目录链接）</title>
        <description>&lt;p&gt;通过 mklink 命令可以创建文件或文件夹的链接，而这种链接跟快捷方式是不一样的。然而我们还可能会遇到其使用过程中的一些坑，本文将整理这些坑并提供解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;0x00-背景介绍mklink&quot;&gt;0x00 背景介绍：mklink&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 可以像创建快捷方式一样建立文件或文件夹的链接，但不同于快捷方式的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 创建的链接绝大多数程序都不会认为那是一个链接，而是一个实实在在的文件或文件夹。&lt;/p&gt;

&lt;p&gt;例如，为 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\OneDrive\Foo&lt;/code&gt; 文件夹创建链接到 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Foo&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\OneDrive\Foo&lt;/code&gt; 中有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;.git&lt;/code&gt; 文件时，绝大多数程序都会以为 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:\Foo&lt;/code&gt; 中也存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;.git&lt;/code&gt; 文件，而且文件内容一模一样。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 可以创建符号链接、硬链接和目录链接。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 即可看到以下这样的帮助信息。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MKLINK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录符号链接。默认为文件&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/H&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建硬链接而非符号链接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/J&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;创建目录联接。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新的符号链接名称。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;指定新链接引用的路径&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;相对或绝对&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体的使用不是本文的重点，可以阅读本文末尾的参考资料了解，这里只给出他们之间的大体区别。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;使用方式&lt;/th&gt;
      &lt;th&gt;适用于&lt;/th&gt;
      &lt;th&gt;快捷方式小箭头&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;不带参数&lt;/td&gt;
      &lt;td&gt;文件&lt;/td&gt;
      &lt;td&gt;有&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;/D&lt;/td&gt;
      &lt;td&gt;文件夹&lt;/td&gt;
      &lt;td&gt;有&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;/J&lt;/td&gt;
      &lt;td&gt;文件夹&lt;/td&gt;
      &lt;td&gt;有&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;/H&lt;/td&gt;
      &lt;td&gt;文件&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;上面的表格顺序，从上到下的行为从越来越像快捷方式到越来越像两个独立的文件夹。&lt;/p&gt;

&lt;h2 id=&quot;0x01-坑powershell-中没有-mklink-命令&quot;&gt;0x01 坑：PowerShell 中没有 mklink 命令&lt;/h2&gt;

&lt;p&gt;是的，PowerShell 中就是中没有 mklink 命令。如果要在 powershell 中使用 mklink，那么得先敲 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 进入 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 之后再使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 命令。&lt;/p&gt;

&lt;p&gt;如果你是一个重度强迫症患者，那么可以编写一个 powershell 的扩展函数来实现：&lt;a href=&quot;https://learn-powershell.net/2013/07/16/creating-a-symbolic-link-using-powershell/&quot;&gt;Creating a Symbolic Link using PowerShell - Learn Powershell - Achieve More&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;0x02-坑权限&quot;&gt;0x02 坑：权限&lt;/h2&gt;

&lt;p&gt;默认我们的用户账户是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Administrators&lt;/code&gt; 组的，会继承它的权限设定。正常情况下，我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mklink&lt;/code&gt; 是可以成功执行的。但如果文件系统的设置比较奇怪或者重装过系统，那么可能出现没有权限的错误。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mklink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/D&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\OneDrive\Foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;你没有足够的权限执行此操作。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，使用管理员权限启动 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 是最简单的做法。不过也可以考虑在 &lt;code class=&quot;highlighter-rouge&quot;&gt;本地安全策略（secpol.msc）\本地策略\用户权利分配&lt;/code&gt; 中添加当前用户。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考链接&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.sinosky.org/mklink-cmd-useful-tips.html&quot;&gt;活用 MKLINK 命令保护、节省你的硬盘 - SinoSky&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/NotBack/article/details/73604292&quot;&gt;关于mklink的/D /J 区别 - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn-powershell.net/2013/07/16/creating-a-symbolic-link-using-powershell/&quot;&gt;Creating a Symbolic Link using PowerShell - Learn Powershell - Achieve More&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/windows_10-files-winpc/win10/8df12869-96f4-4cd1-a914-355e908a6015&quot;&gt;win10 无法运行mklink命令同步onedrive和电脑数据 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/u011583025/article/details/52908508&quot;&gt;Win10下执行mklink提示你没有足够权限执行此操作 - CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 05:01:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/problems-of-mklink.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/problems-of-mklink.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET 中的轻量级线程安全</title>
        <description>&lt;p&gt;对线程安全有要求的代码中，通常会使用锁（lock）。自 .NET 诞生以来就有锁，然而从 .NET Framework 4.0 开始，又诞生了 6 个轻量级的线程安全方案：&lt;code class=&quot;highlighter-rouge&quot;&gt;SpinLock&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SpinWait&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CountdownEvent&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SemaphoreSlim&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ManualResetEventSlim&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Barrier&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;类型&quot;&gt;类型&lt;/h2&gt;

&lt;h3 id=&quot;spinlock-spinwait&quot;&gt;SpinLock, SpinWait&lt;/h3&gt;

&lt;p&gt;SpinLock 被称之为“自旋锁”，SpinWait 称为“自旋等待”，适合在非常轻量的计算中使用。它与普通 lock 的区别在于普通 lock 使用 Win32 内核态对象来实现等待，&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives?wt.mc_id=MVP&quot;&gt;Overview of Synchronization Primitives&lt;/a&gt; 中描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;you can use synchronization primitives that provide fast performance by avoiding expensive reliance on Win32 kernel objects such as wait handles whenever possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在这个过程中，调用线程会挂起，并造成线程的上下文切换，而这是一部分不算小的开销。&lt;/p&gt;

&lt;p&gt;自旋等待则是继续让 CPU 执行此线程，直到锁释放。在这个过程中，此线程会持续占用 CPU 资源，但避免了线程上下文切换。所以，对于短时间的计算采用 SpinLock 实现线程安全会更加高效；而长时间的任务执行会导致占用 CPU 资源从而导致其他任务执行所需的资源减少。&lt;/p&gt;

&lt;h3 id=&quot;countdownevent&quot;&gt;CountdownEvent&lt;/h3&gt;

&lt;p&gt;并行执行一些任务之后，通常还会继续执行一些代码。初始化时设置信号量次数，随后在每一个子任务结束之后设置一个信号量（调用其 &lt;code class=&quot;highlighter-rouge&quot;&gt;Signal&lt;/code&gt; 方法）可以使计数减 1.这样，在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait&lt;/code&gt; 等待的地方就会等计数为 0 后继续执行。&lt;/p&gt;

&lt;h2 id=&quot;semaphoreslim-manualreseteventslim&quot;&gt;SemaphoreSlim, ManualResetEventSlim&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SemaphoreSlim&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ManualResetEventSlim&lt;/code&gt; 是此前 &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ManualResetEvent&lt;/code&gt; 的轻量级版本，从其名字“slim”便能看出来。&lt;/p&gt;

&lt;h2 id=&quot;如何轻量&quot;&gt;如何轻量&lt;/h2&gt;

&lt;p&gt;这些轻量级线程同步方案因为没有使用到 Win32 内核对象，而是在 .NET 内部完成，所以只能进行线程之间的同步，不能进行跨进程同步。如果要完成跨进程的同步，需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Monitor&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Mutex&lt;/code&gt; 这样的方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives?wt.mc_id=MVP&quot;&gt;Overview of Synchronization Primitives - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/?wt.mc_id=MVP&quot;&gt;Thread-Safe Collections - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/clingingboy/article/details/5662735&quot;&gt;.net 4.0新特性-自旋锁(SpinLock) - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/clingingboy/article/details/5662734&quot;&gt;.net 4.0新特性-CountDownEvent - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/ericlippert/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three/&quot;&gt;Atomicity, volatility and immutability are different, part three – Fabulous Adventures In Coding&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/how-to-enable-thread-tracking-mode-in-spinlock?wt.mc_id=MVP&quot;&gt;How to: Enable Thread-Tracking Mode in SpinLock - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.voidcn.com/article/p-pbnmpkmu-bqz.html&quot;&gt;C# SpinWait 实现 - 程序园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.bijishequ.com/detail/359812?p=13-67&quot;&gt;C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )–Thread,Ant,ICP,index,ConsoleWriteLine,CookTasks,cook,particpants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 05:01:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/lightweight-thread-safe-since-dotnet-4.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/lightweight-thread-safe-since-dotnet-4.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>UI 设计中的视觉无障碍设计（色盲眼中的世界以及 UI 使用体验）</title>
        <description>&lt;p&gt;我给博客改了主题色，从 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;这样的&lt;/a&gt; 改成了 &lt;a href=&quot;http://walterlv.gitee.io/&quot;&gt;这样的&lt;/a&gt;；然而我问小伙伴看看效果他却并没有发现改变。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-16-20-39.png&quot; alt=&quot;前后主题色差异&quot; /&gt;&lt;/p&gt;

&lt;p&gt;红绿色盲在亚洲人中占比，男性约 5%，女性则小得多。也就是说，就算仅考虑为国内用户开发应用，这也是很大的一部分用户了。&lt;/p&gt;

&lt;p&gt;本文将通过更加了解色盲（Color Blindness）来指导我们为更多用户提供更好的 UI 设计。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;没有色盲模拟器&quot;&gt;没有色盲模拟器&lt;/h2&gt;

&lt;p&gt;大多数想理解色盲眼中世界的人可能会考虑“色盲模拟器”。在网上搜索“色盲模拟器”，或者英文的“color blindness simulator”可以得到很多的模拟工具，大体思路是将红色、绿色或蓝色分量模拟成另一种颜色分量。&lt;/p&gt;

&lt;p&gt;比如下面这款（&lt;a href=&quot;http://www.color-blindness.com/coblis-color-blindness-simulator/&quot;&gt;Coblis — Color Blindness Simulator&lt;/a&gt;）：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.color-blindness.com/coblis-color-blindness-simulator/&quot;&gt;&lt;img src=&quot;/static/posts/2017-12-09-21-08-53.png&quot; alt=&quot;Coblis — Color Blindness Simulator&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;读者可以试试自己选择一张照片，比如我选择了我拍摄的家门口的照片：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-21-19-50.png&quot; alt=&quot;我家门口拍摄的花&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看完就一个感觉——&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-21-37-48.png&quot; alt=&quot;极度嫌弃&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-21-46-45.png&quot; alt=&quot;可是——真正问色盲者的感受，并不是这样！！！这其实也是一幅富有美感的花的照片（不小心自夸了自己的拍摄技术，求轻拍）。&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而为什么模拟器会模拟成这种图片？因为不可能找到真正能模拟色盲的模拟方法。&lt;/p&gt;

&lt;p&gt;是的——&lt;strong&gt;没有真正的色盲模拟器，这只是正常三色视觉者臆想出来的照片！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;那么，他们眼中的世界到底是怎样？只能想象！&lt;/p&gt;

&lt;h2 id=&quot;理解和想象色盲眼中的世界&quot;&gt;理解和想象色盲眼中的世界&lt;/h2&gt;

&lt;h3 id=&quot;光和颜色&quot;&gt;光和颜色&lt;/h3&gt;

&lt;p&gt;小学科学课本上说“光照在物体上，红色的光射到了眼睛中，于是我们看到的物体是红色”；然而这种说法有些勉强。人类可见光波长范围从约 312.30nm 到 745.40nm 之间，从物理学上光从来都没有颜色的说法，从来无法仅在物理上定义哪一段属于红色、绿色或者青色。那么颜色是怎么来的？——&lt;strong&gt;单纯只是人类的感觉&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-22-02-46.png&quot; alt=&quot;可见光&quot; /&gt;&lt;br /&gt;
▲ 可见光&lt;/p&gt;

&lt;p&gt;而这种感觉从哪里来？从视锥细胞接收到的刺激到大脑皮层的处理这些都是形成感觉的重要部分。对于视锥细胞，维基百科上有说明：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;一般人眼中有三种不同的视锥细胞：第一种主要感受黄绿色，它的最敏感点在565纳米左右；第二种主要感受绿色，它的最敏感点在535纳米左右；第三种主要感受蓝紫色，其最敏感点在420纳米左右。&lt;/p&gt;

  &lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-09-21-54-10.png&quot; alt=&quot;人类(S, M 和 L 类型的)锥状细胞对单色光谱刺激的规范化典型反应&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;▲ 来自于维基百科 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%A2%9C%E8%89%B2&quot;&gt;颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;于是，人类对于可见光部分的所有波长的光，会有各种不同的颜色感觉，比如&lt;code class=&quot;highlighter-rouge&quot;&gt;红色&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;黄色&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;绿色&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;蓝色&lt;/code&gt;。我们没有办法证明每个人对颜色的感知完全一致，不过这里列举的这四种颜色，在全世界所有文化中理解是一致的。这说明遗传带给我们的三种视锥细胞在颜色的感知和理解上占了主导地位。&lt;em&gt;当然，这里说的颜色其实是一段波长范围，在这一段波长范围内，所有文化都会认为它是红色或者黄色或者绿色；虽然有些许不一样，但大体可以说是这种颜色。虽然在中华文化中，青色也会被说成是蓝色的一部分，不过这并不影响人类对这些颜色边界的模糊划分。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;所以，在这一段中，我们将达成一个认知——&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;三种不同的视锥细胞决定了我们对于颜色的感知和识别。&lt;/strong&gt;我们将人类这种通过三种颜色感知细胞对颜色的识别称为三色视觉。&lt;/p&gt;

&lt;h3 id=&quot;双色视觉三色视觉和四色视觉&quot;&gt;双色视觉，三色视觉和四色视觉&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;有些动物有更多的感受光线的细胞种类，比如鸟。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;▲ 来自于维基百科 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%A2%9C%E8%89%B2&quot;&gt;颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;事实上，有更多的感受光线的细胞才是“正常”的、“传统”的。鸟类四种不同的视锥细胞，其主要感受黄、青、蓝和紫外，感受峰值分别是 565nm、508nm、440nm、370nm。而这四种不同的视锥细胞从爬行动物进化而来，事实上即便是现在，大多数脊椎动物依然都是这四种视锥细胞。&lt;/p&gt;

&lt;p&gt;但是，哺乳动物在进化的过程中丢掉了两种——青和紫外，只剩下黄和蓝部分了。也就是说，&lt;strong&gt;大多数哺乳动物都是“色盲”，只有双色视觉&lt;/strong&gt;。而灵长类在进化的过程中因为基因突变，“黄”的部分变成了两种不同的视锥细胞，于是成了三色视觉，主要感受黄色、绿色和蓝色。也就是说，相比于哺乳动物，灵长目的&lt;strong&gt;双色视觉（我们所说的色盲）才是“传统”，而三色视觉其实是基因突变的产物。&lt;/strong&gt;注意鸟类和人类峰值波长的数字，鸟类的四种峰值波长分布比较平均，而人类的三种峰值波长中黄和绿相隔非常近。这也间接说明了这种基因突变的影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-16-17-23.png&quot; alt=&quot;人类和鸟类的波长感知峰值对比&quot; /&gt;&lt;br /&gt;
▲ 人类黄绿色和绿色感知峰值很近，而鸟类的四种感知峰值都很平均&lt;/p&gt;

&lt;p&gt;于是，其实红色和绿色之间的差异本没有那么大，只是因为人类在黄绿色和绿色波长部分出现了两种视锥细胞，于是就把颜色的感知拉得那么大了。即本来差别并不明显的颜色，被大多数人类夸张到红色和绿色这样巨大的差异。&lt;/p&gt;

&lt;h3 id=&quot;从进化的角度来想象色盲眼中的世界&quot;&gt;从进化的角度来想象色盲眼中的世界&lt;/h3&gt;

&lt;p&gt;所以，人类现在的三色视觉其实是&lt;strong&gt;从爬行类的四色视觉“进化”至哺乳类的双色视觉，随后基因突变再次形成灵长类的三色视觉&lt;/strong&gt;的，其中&lt;strong&gt;黄绿色和绿色的感知是同一个基因突变产生，波长感知差异很小的两个部分&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;于是，红绿色盲者和大部分祖先——哺乳类观察绿色和棕色&lt;em&gt;（橙色调暗一点就是棕色）&lt;/em&gt;是很接近的两种颜色。这种接近很像我们观察绿色和波长稍微长一点的绿色一样。于是——哺乳类的毛发颜色其实是保护色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-16-41-09.png&quot; alt=&quot;保护色 1&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-12-10-16-44-30.png&quot; alt=&quot;保护色 2&quot; /&gt;&lt;br /&gt;
▲ 这只是模拟，不代表 TA 们眼中真实的世界&lt;/p&gt;

&lt;p&gt;然后，那个偶然的基因突变让我们硬生生地把波长稍微长一点点的绿色看成了另一种截然不同的颜色。于是，我们一眼便看出了躲藏在万绿丛中的一点棕色。那是多么刺眼的颜色！以至于散落的薰衣草的颜色与周围杂草的颜色对比开始显得暗淡。甚至如果不经过上面的保护色而是直接看到下图你都不一定注意得到薰衣草的存在。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-16-38-52.png&quot; alt=&quot;保护色&quot; /&gt;&lt;br /&gt;
▲ 三色视觉的人类发现了草丛中使用了保护色的哺乳动物&lt;/p&gt;

&lt;p&gt;&lt;em&gt;以上图片来源于网络搜索，模拟由工具生成。如有侵权，请与我联系。&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;从四色视觉的角度来想象色盲眼中的世界&quot;&gt;从四色视觉的角度来想象色盲眼中的世界&lt;/h3&gt;

&lt;p&gt;现在，我们再来脑洞一次。我们硬生生插入一种新的视锥细胞，并为这种人命名为“异人”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-17-11-56.png&quot; alt=&quot;硬生生插入一种视锥细胞&quot; /&gt;&lt;br /&gt;
▲ 硬生生插入一种视锥细胞&lt;/p&gt;

&lt;p&gt;于是他会发现上面的蕨类植物和下面的狗尾草的颜色差异巨大，他们一眼便能将狗尾草从草丛中分离出来，而我们人类需要一点点通过叶子的形状慢慢区分。&lt;/p&gt;

&lt;p&gt;假如异人觉得蕨类植物比较好吃，狗尾草吃着恶心，那么他们便能通过颜色一眼从草丛堆种识别出这两种草来。然后异人有一天突发奇想希望研究我们三色视觉的人类眼中的世界是怎样的，那么他们把他们新增的那种感知颜色模拟成哪种颜色呢？模拟成蕨类植物色？——那他们眼中的草丛都是好吃的甜蜜的颜色，甚至恶心的狗尾草他们居然都能够舒舒服服地看着！模拟成狗尾草色？——那他们眼中的草丛居然都是那么恶心的颜色，他们整天怎么受得了！模拟成中间色？——那看起来就一点也没有草丛的感觉，丑死了！丑死了！一脸嫌弃！&lt;/p&gt;

&lt;h2 id=&quot;ui-设计中的视觉无障碍设计&quot;&gt;UI 设计中的视觉无障碍设计&lt;/h2&gt;

&lt;p&gt;作为三色视觉的程序员和设计师，刚刚我们想象了一把双色视觉的色盲世界，又被四色视觉的异人逼着看叶子的形状找草体验了一把当色盲的感觉。于是，我们便知道可以如何照顾他们的感受，考虑那 5% 的用户做出更好的 UI 设计。&lt;/p&gt;

&lt;h3 id=&quot;安全色&quot;&gt;安全色&lt;/h3&gt;

&lt;p&gt;在有多种颜色方案可选的时候，我们可以挑选出那些对色盲友好的颜色方案。&lt;/p&gt;

&lt;p&gt;当然我们并不指望能够照顾到所有的色盲人群，因为那样就没有任何必要去使用颜色了，超过 99% 的用户将无颜色可用，但这样的性价比实在太低。但是我们可以尽量照顾到红绿色盲人群，因为这在色盲人种的比例是最高的，剩余的人群占比非常低，我们将考虑用非颜色元素来实现无障碍设计。&lt;/p&gt;

&lt;p&gt;那么，如何挑选呢？&lt;/p&gt;

&lt;p&gt;红色部分可以不变，绿色部分可以考虑像蓝色部分偏移，就像现在的红绿灯设计那样。色盲者接触到的颜色信息会少一些，这使得能分辨出的颜色差异会更加明显。&lt;/p&gt;

&lt;p&gt;在团队的 UI 设计中，建议制作一些产品标准色之后通过色盲模拟器检验。此后使用这些标准色。&lt;/p&gt;

&lt;h3 id=&quot;非颜色辅助元素&quot;&gt;非颜色辅助元素&lt;/h3&gt;

&lt;p&gt;如果某些 UI 元素&lt;strong&gt;仅用&lt;/strong&gt;颜色来区分，那么对色盲来说这样的 UI 将非常难用。&lt;/p&gt;

&lt;p&gt;比如下面这个验证数学公式的方式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-18-03-10.png&quot; alt=&quot;验证数学公式&quot; /&gt;&lt;br /&gt;
▲ 红色代表错误，绿色代表编辑中&lt;/p&gt;

&lt;p&gt;如果用模拟器模拟，将得到这样的图像：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-18-03-59.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 色盲模拟器模拟的输入框验证 1
&lt;img src=&quot;/static/posts/2017-12-10-18-04-14.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 色盲模拟器模拟的输入框验证 2&lt;/p&gt;

&lt;p&gt;也就是说，找到错误的公式会像我们前面从草丛种分辨出狗尾草一样困难。&lt;/p&gt;

&lt;p&gt;但如果我们增加一点符号显示，或者在公式旁边放上文字提示（其中一种就够了），这样的问题就能立刻解决。比如这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-18-11-03.png&quot; alt=&quot;增加符号和文字提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时再模拟，也能清楚地找到错误的公式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-18-13-05.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 色盲模拟器模拟的输入框验证 1&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-10-18-13-10.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 色盲模拟器模拟的输入框验证 2&lt;/p&gt;

&lt;p&gt;另外，前面我标注人类、鸟类和异人的波长点用的是符号而不是颜色。在制作图表的时候，通常也应该考虑使用符号代替颜色。&lt;/p&gt;

&lt;h3 id=&quot;推荐一些工具用来手工检查界面是否做到了无障碍设计&quot;&gt;推荐一些工具用来手工检查界面是否做到了无障碍设计&lt;/h3&gt;

&lt;p&gt;本文开头我们说到&lt;strong&gt;色盲模拟器并不能模拟色盲眼中的世界&lt;/strong&gt;，但是我们却可以用这些模拟器来评估我们的 UI 设计是否能让色盲者无障碍地使用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.toptal.com/designers/colorfilter&quot;&gt;Toptal Color Blind Filter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.color-blindness.com/coblis-color-blindness-simulator/&quot;&gt;Coblis — Color Blindness Simulator - Colblindor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考与引用&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%A2%9C%E8%89%B2&quot;&gt;颜色 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%89%B2%E7%9B%B2&quot;&gt;色盲 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/20046763/answer/50002728&quot;&gt;为什么没有绿色毛发的哺乳动物？ - 小城的回答 - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/23542971&quot;&gt;在红绿色盲眼里看到的世界是怎样的？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/31111004?utm_medium=social&amp;amp;utm_source=wechat_session&quot;&gt;无障碍设计：为色盲用户多走一步&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 05:00:53 +0000</pubDate>
        <link>https://blog.walterlv.com/post/ui-design-for-color-blindness.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/ui-design-for-color-blindness.html</guid>
        
        
        <category>ux</category>
        
        <category>ui</category>
        
      </item>
    
      <item>
        <title>C#/.NET 中的契约式编程，以及 ReSharper 为我们提供的契约特性</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;将文档放到代码里面，文档才会及时地更新！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;微软从 .NET Framework 4.0 开始，增加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Diagnostics.Contracts&lt;/code&gt; 命名空间，用来把契约文档融入代码。然而后面一直不冷不热，Visual Studio 都没天然支持。&lt;/p&gt;

&lt;p&gt;ReSharper 也提供了 ReSharper Annotations，在 ReSharper 插件工作的情况下能够进行静态契约的验证。&lt;/p&gt;

&lt;p&gt;C#8.0 的可空引用类型是 Roslyn 对 null 的验证，这个可能更加强大，既可以是编译警告，也可以是编译错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;契约式编程&quot;&gt;契约式编程&lt;/h2&gt;

&lt;p&gt;当你调用某个类库里面的方法时，你如何能够知道传入的参数是否符合规范？如何能够知道方法调用结束之后是否要对结果进行判断？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 对于上面的方法，你知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 传入参数是合理的吗？返回的参数需要判空吗？&lt;/p&gt;

&lt;p&gt;代码的编写者可能是这么写的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 后续逻辑。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有些静态代码检查工具也许可以根据这里的参数判断代码块来认定为此处的参数不能为 null，但这种判断代码无处不在，静态检查工具如何能够有效地捕获每一处的检查呢？难道我们真的要去翻阅文档吗？然而除非是专门提供 SDK 的团队，否则文档通常都会滞后于代码，那么对于这些契约的修改可能就不太准确。&lt;/p&gt;

&lt;p&gt;于是，契约式编程就应运而生。&lt;/p&gt;

&lt;p&gt;它将前置条件（Precondition）、后置条件（Postcondition）、不变量（Invariant）等代码分离出来，按照特定的格式编写以便能够被静态检查工具分析出来。&lt;/p&gt;

&lt;p&gt;有了静态分析工具以及契约代码的帮助，Visual Studio 的智能感知提示将能够直接告诉我们代码编写的潜在问题，而不必等到运行时再抛出异常，那时将降低开发效率，将增加生产环境运行的风险。&lt;/p&gt;

&lt;h2 id=&quot;几种不同的契约方法&quot;&gt;几种不同的契约方法&lt;/h2&gt;

&lt;h3 id=&quot;resharper-annotations&quot;&gt;ReSharper Annotations&lt;/h3&gt;

&lt;p&gt;ReSharper 并没有将其称之为“契约”，因为它真的只是“文档级别”的约束，只会在写代码的时候具备一定程度的静态分析能力以便给出提示，并不提供运行时的检查。不过，ReSharper 会为我们生成运行时检查的代码。只要是装了 ReSharper 插件并用它写过代码的，应该都见过 ReSharper Annotations 了，因为它会在我们试图添加契约代码时自动添加契约标记（Attribute）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-20-22-19-10.png&quot; alt=&quot;提示生成&quot; /&gt;&lt;br /&gt;
▲ 生成 ReSharper Annotations&lt;/p&gt;

&lt;p&gt;如果错过了首次提示，可以在 ReSharper 的设置界面中生成 Annotations 的代码。（复制一份代码然后新建一个文件粘贴。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-20-22-14-55.png&quot; alt=&quot;手动生成&quot; /&gt;&lt;br /&gt;
▲ 手动生成 ReSharper Annotations&lt;/p&gt;

&lt;h4 id=&quot;resharper-中常用的契约-attribute&quot;&gt;ReSharper 中常用的契约 Attribute&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;CanBeNull
    &lt;ul&gt;
      &lt;li&gt;表示参数或返回值可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;CannotApplyEqualityOperator
    &lt;ul&gt;
      &lt;li&gt;表示某个类型的相等比较不应该用 &lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt;，而应该用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Equals&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ItemCanBeNull
    &lt;ul&gt;
      &lt;li&gt;表示集合参数或集合返回值里某一项可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
      &lt;li&gt;或者表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; 返回值中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ItemNotNull
    &lt;ul&gt;
      &lt;li&gt;表示集合参数或集合返回值里每一项都不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
      &lt;li&gt;或者表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; 返回值中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LinqTunnel
    &lt;ul&gt;
      &lt;li&gt;表示某个方法就像 linq 方法一样。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LocalizationRequired
    &lt;ul&gt;
      &lt;li&gt;表示参数字符串需要被本地化。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;NotNull
    &lt;ul&gt;
      &lt;li&gt;表示参数或返回值不可能为 null。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;PathReference
    &lt;ul&gt;
      &lt;li&gt;表示参数字符串是一个路径。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Pure
    &lt;ul&gt;
      &lt;li&gt;表示方法不会修改任何状态（这意味着如果连返回值都不用，那调用了也相当于什么都没做）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;RegexPattern
    &lt;ul&gt;
      &lt;li&gt;表示参数字符串是一个正则表达式（会被 ReSharper 代码着色）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;还有 100+ 个……&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;ContractAnnotation
    &lt;ul&gt;
      &lt;li&gt;详见 &lt;a href=&quot;https://www.jetbrains.com/help/resharper/Contract_Annotations.html&quot;&gt;Contract Annotations - Help - ReSharper&lt;/a&gt;，可以使用约定的语法写出更复杂的契约。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我的朋友&lt;a href=&quot;https://lindexi.github.io/lindexi/&quot;&gt;林德熙&lt;/a&gt;在 &lt;a href=&quot;https://lindexi.github.io/lindexi/post/%E4%BD%BF%E7%94%A8-Resharper-%E7%89%B9%E6%80%A7.html&quot;&gt;使用 Resharper 特性&lt;/a&gt; 一文中有这些契约对编写代码的更详细的效果描述和截图。&lt;/p&gt;

&lt;h3 id=&quot;systemdiagnosticscontracts&quot;&gt;System.Diagnostics.Contracts&lt;/h3&gt;

&lt;p&gt;此命名空间下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Contract&lt;/code&gt; 类型定义了几个方法，覆盖了我们编写一个方法所要遵循的契约模式。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// * 要开始此任务必须先满足某些条件（Requires，RequiresAlways，EndContractBlock）&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 做一些操作。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// * 此时认定一定满足某个条件（Assume）&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 继续执行一些操作。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// * 操作执行完后一定满足某组条件（Ensures，EnsuresOnThrows）&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码中，星号（*）表示契约代码，其他表示方法内的普通代码。一个典型的例子如以下代码所示：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// * 要开始此任务必须先满足某些条件（Requires，EndContractBlock）&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Requires&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 做一些操作。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// * 此时认定一定满足某个条件（Assume）&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 继续执行一些操作。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// * 操作执行完后一定满足某组条件（Ensures，EnsuresOnThrows）&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnsuresOnThrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;Requires&lt;/code&gt; 是真的会抛出异常的，但 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assume&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnsuresOnThrow&lt;/code&gt; 是需要写条件编译符为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CONTRACTS_FULL&lt;/code&gt; 的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-04-14-26-42.png&quot; alt=&quot;前置条件失败&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或者，这样用普通的抛异常的方式。如果使用普通方式抛出异常，需要遵循 &lt;code class=&quot;highlighter-rouge&quot;&gt;if-then-throw&lt;/code&gt; 的模式，即有问题立刻就抛出异常。例如下面对 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的判断就符合这样的模式。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// * 要开始此任务必须先满足某些条件（Requires，EndContractBlock）&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 做一些操作。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// * 此时认定一定满足某个条件（Assume）&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 继续执行一些操作。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// * 操作执行完后一定满足某组条件（Ensures，EnsuresOnThrows）&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnsuresOnThrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然也可以不止是这样简单的判断，也可以调用其他方法，但要求方法必须是 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Pure]&lt;/code&gt; 方法，即方法执行完之后，除了返回一个值之外，不改变应用程序的任何状态。&lt;/p&gt;

&lt;p&gt;对此契约的静态分析微软有提供工具：&lt;a href=&quot;https://github.com/Microsoft/CodeContracts&quot;&gt;Microsoft/CodeContracts: Source code for the CodeContracts tools for .NET&lt;/a&gt;，ReSharper 对此也有一丁点儿的支持。&lt;/p&gt;

&lt;h3 id=&quot;roslyn&quot;&gt;Roslyn&lt;/h3&gt;

&lt;p&gt;Roslyn 相比于任何第三方契约的优势在于它甚至能在语法层面形成契约（&lt;a href=&quot;/post/nullable-reference-in-csharp&quot;&gt;比如 C#8.0 中的可空引用类型&lt;/a&gt;）。&lt;/p&gt;

&lt;h2 id=&quot;实际应用&quot;&gt;实际应用&lt;/h2&gt;

&lt;p&gt;事实上在 GitHub 中，使用各种契约的都有，不过以 ReSharper Annotations 和 System.Diagnostics.Contracts 的居多；C#8.0 的可空引用类型等到 8.0 发布以后再看吧。&lt;/p&gt;

&lt;p&gt;在实际应用中，并没有严格的说哪一个更好哪一个一般，两者都可以用，只要我们有分析和提示此契约的工具，就可以在项目中推行开来。&lt;/p&gt;

&lt;p&gt;但是，&lt;strong&gt;基于契约编写代码的模式却能帮助我们写出更加健壮的代码来&lt;/strong&gt;。也就是说，用哪个并不重要，重要的是——&lt;strong&gt;用起来&lt;/strong&gt;！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts?wt.mc_id=MVP&quot;&gt;Code Contracts - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/lucifer1982/archive/2009/03/21/1418642.html&quot;&gt;.NET 4.0 中的契约式编程 - Angel Lucifer - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.uml.org.cn/net/201510303.asp&quot;&gt;C# 中参数验证方式的演变 -.net-火龙果软件工程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/help/resharper/Contract_Annotations.html&quot;&gt;Contract Annotations - Help - ReSharper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Mar 2019 04:57:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/contracts-in-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/contracts-in-csharp.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>我收集的各种公有 NuGet 源</title>
        <description>&lt;p&gt;本文收集我发现的各种公共 NuGet 源。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何添加本文介绍的-nuget-源&quot;&gt;如何添加本文介绍的 NuGet 源？&lt;/h2&gt;

&lt;p&gt;请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-custom-nuget-source&quot;&gt;全局或为单独的项目添加自定义的 NuGet 源&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;官方-nuget-源&quot;&gt;官方 NuGet 源&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;官方源
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://api.nuget.org/v3/index.json&quot;&gt;https://api.nuget.org/v3/index.json&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;官方离线本地源
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;官方网站：&lt;a href=&quot;https://www.nuget.org/&quot;&gt;https://www.nuget.org/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;dotnet feed
    &lt;ul&gt;
      &lt;li&gt;包含 &lt;a href=&quot;https://github.com/dotnet&quot;&gt;.NET Foundation&lt;/a&gt; 中各种项目的日构建包&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json&quot;&gt;https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nuget-镜像&quot;&gt;NuGet 镜像&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;华为云 huaweicloud &lt;a href=&quot;https://mirrors.huaweicloud.com/&quot;&gt;https://mirrors.huaweicloud.com/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://repo.huaweicloud.com/repository/nuget/v3/index.json&quot;&gt;https://repo.huaweicloud.com/repository/nuget/v3/index.json&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Telerik NuGet
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://nuget.telerik.com/nuget&quot;&gt;https://nuget.telerik.com/nuget&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;其他-nuget-源&quot;&gt;其他 NuGet 源&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.myget.org/gallery&quot;&gt;MyGet&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;这是一个很激进的 NuGet 源，包含各种日构建包（其中包括 .NET Standard 或者 .NET Core 等库的日构建版本），所以如果你希望尝试最新的 API 最新的功能，最好设置此 NuGet 源。&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&gt;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;私有-nuget-源&quot;&gt;私有 NuGet 源&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gemfury.com/help/nuget-server/&quot;&gt;Installing private NuGet packages · Gemfury Dev Center&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;这是私有 NuGet 源，你可以在上面取得独立的 NuGet 链接，上面只有自己私有的 NuGet 包&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nuget-网站&quot;&gt;NuGet 网站&lt;/h2&gt;

&lt;p&gt;呃……这部分只是 NuGet 网站而已，你可以在这里浏览 NuGet 包的各种信息，但是它不提供源。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.fuget.org/&quot;&gt;FuGet Gallery - https://www.fuget.org/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;此项目开源：&lt;a href=&quot;https://github.com/praeclarum/FuGetGallery&quot;&gt;praeclarum/FuGetGallery: An alternative web UI for browsing nuget packages&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 12 Mar 2019 04:41:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/public-nuget-sources.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/public-nuget-sources.html</guid>
        
        
        <category>dotnet</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>在 csproj 文件中使用系统环境变量的值（示例将 dll 生成到 AppData 目录下）</title>
        <description>&lt;p&gt;Windows 系统以及很多应用程序会考虑使用系统的环境变量来传递一些公共的参数或者配置。Windows 资源管理器使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%var%&lt;/code&gt; 来使用环境变量，那么我们能否在 Visual Studio 的项目文件中使用环境变量呢？&lt;/p&gt;

&lt;p&gt;本文介绍如何在 csproj 文件中使用环境变量。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;遇到的问题&quot;&gt;遇到的问题&lt;/h2&gt;

&lt;p&gt;在 Windows 资源管理器中，我们可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%AppData%&lt;/code&gt; 进入到用户的漫游路径。我正在为 &lt;a href=&quot;http://easinote.seewo.com/&quot;&gt;希沃白板5 为互动教学而生 - 课件制作神器&lt;/a&gt; 编写插件，于是需要将插件放到指定目录：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\Seewo\EasiNote5\Walterlv.Presentation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 Windows 资源管理器中可以直接输入以上文字进入对应的目录（当然需要确保存在）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-10-18-51-38.png&quot; alt=&quot;插件目录&quot; /&gt;&lt;/p&gt;

&lt;p&gt;更多关于路径的信息可以参考：&lt;a href=&quot;/post/all-kinds-of-paths-in-uwp&quot;&gt;UWP 中的各种文件路径（用户、缓存、漫游、安装……） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;然而，为了调试方便，我最好在 Visual Studio 中编写的时候就能直接输出到插件目录。&lt;/p&gt;

&lt;p&gt;于是，我需要将 Visual Studio 的调试目录设置为以上目录，但是以上目录中包含环境变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;%AppData%&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;在-visual-studio-中修改输出路径&quot;&gt;在 Visual Studio 中修改输出路径&lt;/h2&gt;

&lt;p&gt;如果直接在 csproj 中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;%AppData%&lt;/code&gt;，那么 Visual Studio 会原封不动地创建一个这样的文件夹。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-10-18-57-40.png&quot; alt=&quot;一个诡异的文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;实际上，Visual Studio 是天然支持环境变量的。直接使用 MSBuild 获取属性的语法即可获取环境变量的值。&lt;/p&gt;

&lt;p&gt;也就是说，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(AppData)&lt;/code&gt; 即可获取到其值。在我的电脑上是 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Users\lvyi\AppData\Roaming&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;于是，在 csproj 中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;OutputPath&lt;/code&gt; 即可正确输出我的插件到目标路径。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputPath&amp;gt;&lt;/span&gt;$(AppData)\Seewo\EasiNote5\Extensions\Walterlv.Presentation&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputPath&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppendTargetFrameworkToOutputPath&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppendTargetFrameworkToOutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，我额外设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppendTargetFrameworkToOutputPath&lt;/code&gt; 属性，这是避免 &lt;code class=&quot;highlighter-rouge&quot;&gt;net472&lt;/code&gt; 出现在了目标输出路径中。你可以阅读我的另一篇博客了解更多关于输出路径的问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-properties-that-affetcs-project-output-path&quot;&gt;如何更精准地设置 C# / .NET Core 项目的输出路径？（包括添加和删除各种前后缀） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 12 Mar 2019 03:53:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/environment-variables-in-csproj.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/environment-variables-in-csproj.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>为 WPF 程序添加 Windows 跳转列表的支持</title>
        <description>&lt;p&gt;Windows 跳转列表是自 Windows 7 时代就带来的功能，这一功能是跟随 Windows 7 的任务栏而发布的。当时应用程序要想用上这样的功能需要调用 shell 提供的一些 API。&lt;/p&gt;

&lt;p&gt;然而在 WPF 程序中使用 Windows 跳转列表功能非常简单，在 XAML 里面就能完成。本文将介绍如何让你的 WPF 应用支持 Windows 跳转列表功能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个简单的跳转列表程序&quot;&gt;一个简单的跳转列表程序&lt;/h2&gt;

&lt;p&gt;新建一个 WPF 程序，然后直接在 App.xaml 中添加跳转列表的代码。这里为了更快上手，我直接贴出整个 App.xaml 的代码。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.WindowsTasks.App&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.WindowsTasks&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;StartupUri=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MainWindow.xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;JumpList.JumpList&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;JumpList&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ShowRecentCategory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ShowFrequentCategory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;JumpTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;启动新窗口&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Description=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;启动一个新的空窗口&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;JumpTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;修改 walterlv 的个性化设置&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Description=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;打开个性化设置页面并定位到 walterlv 的设置&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;IconResourcePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C:\Windows\System32\wmploc.dll&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IconResourceIndex=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;17&quot;&lt;/span&gt;
                      &lt;span class=&quot;na&quot;&gt;Arguments=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--account&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/JumpList&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/JumpList.JumpList&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;顺便的，我加了一个简单的图标，这样不至于显示一个默认的应用图标。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-21-21-45-13.png&quot; alt=&quot;添加的简单的图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;运行此程序后就可以在任务栏上右击的时候看到跳转列表：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-21-21-42-05.png&quot; alt=&quot;运行后看到的跳转列表&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这段程序中，我们添加了两个“任务”，在跳转列表中有一个“任务”分类。因为我的系统是英文，所以显示的是“Task”。&lt;/p&gt;

&lt;p&gt;在任务分类中，有两个“任务”，&lt;code class=&quot;highlighter-rouge&quot;&gt;启动新窗口&lt;/code&gt; 以及 &lt;code class=&quot;highlighter-rouge&quot;&gt;修改 walterlv 的个性化设置&lt;/code&gt;。第一个任务只设了标题和鼠标移上去的提示信息，于是显示的图标就是应用本身的图标，点击之后也是启动任务自己。第二个任务设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Arguments&lt;/code&gt; 参数，于是点击之后会带里面设置的参数启动自己；同时设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IconResourcePath&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;IconResourceIndex&lt;/code&gt; 用于指定图标。&lt;/p&gt;

&lt;p&gt;这种图标的指定方式是 Windows 系统中非常常用的方式。你可以在我的另一篇博客中找到各种各样系统自带的图标；至于序号，则是自己去数。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/where-is-the-windows-10-native-icons&quot;&gt;Windows 10 自带那么多图标，去哪里找呢？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;定制跳转列表的功能&quot;&gt;定制跳转列表的功能&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpList&lt;/code&gt; 有两个属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowRecentCategory&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowFrequentCategory&lt;/code&gt;，如果指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 则表示操作系统会自动为我们保存此程序最近使用的文件的最频繁使用的文件。&lt;/p&gt;

&lt;p&gt;Windows 的跳转列表有两种不同的列表项，一种是“任务”，另一种是文件。至于这两种不同的列表项如何在跳转列表中安排，则是操作系统的事情。&lt;/p&gt;

&lt;p&gt;这两种不同的列表项对应的类型分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpTask&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpPath&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpTask&lt;/code&gt; 可以理解为这就是一个应用程序的快捷方式，可以指定应用程序的路径（&lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt;）、工作目录（&lt;code class=&quot;highlighter-rouge&quot;&gt;WorkingDirectory&lt;/code&gt;）、启动参数（&lt;code class=&quot;highlighter-rouge&quot;&gt;Arguments&lt;/code&gt;）和图标（&lt;code class=&quot;highlighter-rouge&quot;&gt;IconResourcePath&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;IconResourceIndex&lt;/code&gt;）。如果不指定路径，那么就默认为当前程序。也可以指定显示的名称（&lt;code class=&quot;highlighter-rouge&quot;&gt;Title&lt;/code&gt;）和鼠标移上去可以看的描述（&lt;code class=&quot;highlighter-rouge&quot;&gt;Description&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpPath&lt;/code&gt; 则是一个路径，可以是文件或者文件夹的路径。通常用来作为最近使用文件的展示。&lt;strong&gt;特别说明&lt;/strong&gt;：你必须关联某种文件类型这种类型的文件才会显示到 &lt;code class=&quot;highlighter-rouge&quot;&gt;JumpPath&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpTask&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;JumpPath&lt;/code&gt; 都有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomCategory&lt;/code&gt; 属性可以指定类别。对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;JumpTask&lt;/code&gt;，如果不指定类别，那么就会在默认的“任务”（Task）类别中。对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;JumpPath&lt;/code&gt;，如果不指定类别，就在最近的文件中。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JumpTask&lt;/code&gt; 如果不指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Title&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomCategory&lt;/code&gt; 属性，那么他会成为一个分隔符。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.windows.shell.jumplist&quot;&gt;JumpList Class (System.Windows.Shell) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/taskbar-extensions&quot;&gt;Taskbar Extensions - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 10 Mar 2019 13:30:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-application-with-jumplist.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-application-with-jumplist.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Windows 上的应用程序在运行期间可以给自己改名（可以做 OTA 自我更新）</title>
        <description>&lt;p&gt;程序如何自己更新自己呢？你可能会想到启动一个新的程序或者脚本来更新自己。然而 Windows 操作系统允许一个应用程序在运行期间修改自己的名称甚至移动自己到另一个文件夹中。利用这一点，我们可以很简单直接地做程序的 OTA 自动更新。&lt;/p&gt;

&lt;p&gt;本文将介绍示例程序运行期间改名并解释其原理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;在程序运行期间手工改名&quot;&gt;在程序运行期间手工改名&lt;/h2&gt;

&lt;p&gt;我们写一个简单的程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-17-26-25.png&quot; alt=&quot;简单的程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将它运行起来，然后删除。我们会发现无法删除它。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-17-27-12.png&quot; alt=&quot;无法删除程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，我们却可以很轻松地在资源管理器中对它进行改名，甚至将它从一个文件夹中移动到另一个文件夹中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-17-28-14.png&quot; alt=&quot;已经成功改名&quot; /&gt;&lt;/p&gt;

&lt;p&gt;值得注意的是，你不能跨驱动器移动此文件。&lt;/p&gt;

&lt;h2 id=&quot;不止是-exe-文件dll-文件也是可以改名的&quot;&gt;不止是 exe 文件，dll 文件也是可以改名的&lt;/h2&gt;

&lt;p&gt;实际上，不止是 exe 文件，在 exe 程序运行期间，即使用到了某些 dll 文件，这些 dll 文件也是可以改名的。&lt;/p&gt;

&lt;p&gt;当然，一个 exe 的运行不一定在启动期间就加载好了所有的 dll，所以如果你在 exe 启动之后，某个 dll 加载之前改了那个 dll 的名称，那么会出现找不到 dll 的情况，可能导致程序崩溃。&lt;/p&gt;

&lt;h2 id=&quot;为什么-windows-上的可执行程序可以在运行期间改名&quot;&gt;为什么 Windows 上的可执行程序可以在运行期间改名？&lt;/h2&gt;

&lt;p&gt;Windows 的文件系统由两个主要的表示结构：一个是目录信息，它保存有关文件的元数据（如文件名、大小、属性和时间戳）；第二个是文件的数据链。&lt;/p&gt;

&lt;p&gt;当运行程序加载一个程序集的时候，会为此程序集创建一个内存映射文件。为了优化性能，往往只有实际用到的部分才会被加入到内存映射文件中；当需要用到程序集文件中的某块数据时，Windows 操作系统就会将需要的部分加载到内存中。但是，内存映射文件只会锁定文件的数据部分，以保证文件文件的数据不会被其他的进程修改。&lt;/p&gt;

&lt;p&gt;这里就是关键，内存映射文件只会锁定文件的数据部分，而不会锁住文件元数据信息。这意味着你可以随意修改这些元数据信息而不会影响程序的正常运行。这就包括你可以修改文件名，或者把程序从一个文件夹下移动到另一个文件夹去。&lt;/p&gt;

&lt;p&gt;但是跨驱动器移动文件，就意味着需要在原来的驱动器下删除文件，而这个操作会影响到文件的数据部分，所以此操作不被允许。&lt;/p&gt;

&lt;h2 id=&quot;编写一个程序在运行期间自动改名&quot;&gt;编写一个程序在运行期间自动改名&lt;/h2&gt;

&lt;p&gt;一般来说，需要 OTA 更新的程序是客户端程序，所以实际上真正需要此代码的是客户端应用。以下代码中我使用 .NET Core 3.0 来编写一个给自己改名的 WPF 程序。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows.Updater&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartupEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OldUpdater.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略的代码：将新下载下载的程序改名成 fileName。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，程序自己在运行后会改名。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-18-53-01.png&quot; alt=&quot;程序已经自己改名&quot; /&gt;&lt;/p&gt;

&lt;p&gt;顺便的，以上代码仅适用于 .NET Framework 的桌面应用程序或者 .NET Core 3.0 的桌面应用程序。如果是 .NET Core 2.x，那么以上代码在获取到进程名称的时候可能是 dotnet.exe（已发布的 .NET Core 程序除外）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/14775626/6233938&quot;&gt;c# - Why does rename a loaded .net assembly work? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/questions/488127/why-can-i-rename-a-running-executable-but-not-delete-it&quot;&gt;windows 7 - Why can I rename a running executable, but not delete it? - Super User&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3365347/how-can-we-overwrite-exe-files-while-users-are-running-them&quot;&gt;deployment - How can we overwrite EXE files while users are running them? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 10 Mar 2019 13:30:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/rename-executable-self-when-running.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/rename-executable-self-when-running.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化</title>
        <description>&lt;p&gt;最近我大幅度重构了我一个库的项目结构，使之使用最新的项目文件格式（基于 Microsoft.NET.Sdk）并使用 SourceYard 源码包来打包其中的一些公共代码。不过，最终生成了一个新的 dll 之后却心有余悸，不知道我是否删除或者修改了某些 API，是否可能导致我原有库的使用者出现意料之外的兼容性问题。&lt;/p&gt;

&lt;p&gt;另外，准备为一个产品级项目更新某个依赖库，但不知道更新此库对我们的影响有多大，希望知道目前版本和希望更新的版本之间的 API 差异。&lt;/p&gt;

&lt;p&gt;索性发现了 JustAssembly 可以帮助我们分析程序集 API 的变化。本文将介绍如何使用 JustAssembly 来分析不同版本程序集 API 的变化。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;下载和安装-justassembly&quot;&gt;下载和安装 JustAssembly&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/telerik/justassembly&quot;&gt;JustAssembly&lt;/a&gt; 是 &lt;a href=&quot;https://github.com/telerik&quot;&gt;Telerik&lt;/a&gt; 开源的一款程序集分析工具。&lt;/p&gt;

&lt;p&gt;你可以去它的官网下载并安装：&lt;a href=&quot;https://www.telerik.com/justassembly&quot;&gt;Assembly Diff Tool for .NET - JustAssembly&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;开始比较&quot;&gt;开始比较&lt;/h2&gt;

&lt;p&gt;启动 JustAssembly，在一开始丑陋（逃）的界面中选择旧的和新的 dll 文件，然后点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Load&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-16-59-29.png&quot; alt=&quot;选择旧的和新的 dll 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，你就能看到新版本的 API 相比于旧版本的差异了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-24-17-03-03.png&quot; alt=&quot;新版本的 API 相比于旧版本的差异&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;关于比较结果的说明&quot;&gt;关于比较结果的说明&lt;/h2&gt;

&lt;p&gt;在差异界面中，差异有以下几种显示：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;没有差异
    &lt;ul&gt;
      &lt;li&gt;以白色底显示&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;新增
    &lt;ul&gt;
      &lt;li&gt;以绿色底辅以 &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; 符号显示&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;删除
    &lt;ul&gt;
      &lt;li&gt;以醒目的红色底辅以 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; 符号显示&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;有部分差异
    &lt;ul&gt;
      &lt;li&gt;以蓝紫色底辅以 &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt; 符号显示&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里可能需要说明一下“部分差异”：由于差异是以树状结构显示的，所以如果子节点有新增，那么父节点因为既有新增又存在未修改的节点，所以会以“有部分差异”的方式显示。&lt;/p&gt;

&lt;p&gt;对于每一个差异，双击可以去看差异的代码详情。&lt;/p&gt;

&lt;p&gt;上图我的 SourceFusion 项目在版本更新的时候只有新增的 API，没有修改和删除的 API，所以还是一个比较健康的 API 更新。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/telerik/justassembly&quot;&gt;telerik/JustAssembly: Assembly Diff and Analysis Tool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.telerik.com/justassembly&quot;&gt;Assembly Diff Tool for .NET - JustAssembly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 10 Mar 2019 13:30:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/compare-api-between-two-assemblies-using-just-assembly.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/compare-api-between-two-assemblies-using-just-assembly.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>详解 .NET 反射中的 BindingFlags 以及常用的 BindingFlags 使用方式</title>
        <description>&lt;p&gt;使用 .NET 的反射 API 时，通常会要求我们传入一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;BindingFlags&lt;/code&gt; 参数用于指定反射查找的范围。不过如果对反射不熟的话，第一次写反射很容易写错导致找不到需要的类型成员。&lt;/p&gt;

&lt;p&gt;本文介绍 &lt;code class=&quot;highlighter-rouge&quot;&gt;BindingFlags&lt;/code&gt; 中的各个枚举标记的含义、用途，以及常用的组合使用方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;所有的-bindingflags&quot;&gt;所有的 BindingFlags&lt;/h2&gt;

&lt;h3 id=&quot;默认值&quot;&gt;默认值&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 默认值&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;查找&quot;&gt;查找&lt;/h3&gt;

&lt;p&gt;这些标记用于反射的时候查找类型成员：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 表示查找的时候，需要忽略大小写。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IgnoreCase&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 仅查找此特定类型中声明的成员，而不会包括这个类继承得到的成员。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DeclaredOnly&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 仅查找类型中的实例成员。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 仅查找类型中的静态成员。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Static&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 仅查找类型中的公共成员。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 仅查找类型中的非公共成员（internal protected private）&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;NonPublic&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 会查找此特定类型继承树上得到的静态成员。但仅继承公共（public）静态成员和受保护（protected）静态成员；不包含私有静态成员，也不包含嵌套类型。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;FlattenHierarchy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;调用&quot;&gt;调用&lt;/h3&gt;

&lt;p&gt;这些标记用于为 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeMember&lt;/code&gt; 方法提供参数，告知应该如何反射调用一个方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 调用方法。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;InvokeMethod&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 创建实例。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CreateInstance&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 获取字段的值。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetField&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 设置字段的值。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SetField&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 获取属性的值。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetProperty&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 设置属性的值。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;p&gt;接下来下面的部分就不是那么常用的了。&lt;/p&gt;

&lt;p&gt;这些标记用于为 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeMember&lt;/code&gt; 方法提供参数，但是仅在调用一个 COM 组件的时候才应该使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;PutDispProperty&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PutRefDispProperty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ExactBinding&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SuppressChangeType&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;OptionalParamBinding&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面是一些杂项……&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 忽略返回值（在 COM 组件的互操作中使用）&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IgnoreReturn&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 反射调用方法时如果出现了异常，通常反射会用 TargetInvocationException 包装这个异常。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 此标记用于禁止把异常包装到 TargetInvocationException 中。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DoNotWrapExceptions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;你可能会有的疑问&quot;&gt;你可能会有的疑问&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;如果 A 程序集对 B 程序集内部可见（&lt;code class=&quot;highlighter-rouge&quot;&gt;InternalsVisibleTo(&quot;B&quot;)&lt;/code&gt;），那么 B 在反射查找 A 的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 成员的查找应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Public&lt;/code&gt; 还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NonPublic&lt;/code&gt; 标记呢？
    &lt;ul&gt;
      &lt;li&gt;依然是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NonPublic&lt;/code&gt; 标记。&lt;/li&gt;
      &lt;li&gt;因为反射的是程序集的元数据，这是静态的数据，跟运行时状态是无关的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;常用的组合&quot;&gt;常用的组合&lt;/h2&gt;

&lt;p&gt;从上面的解释中可以发现，这个类型的设计其实是有问题的，不符合单一职责原则。所以我们会在不同的使用场景下使用不同区域的组合。&lt;/p&gt;

&lt;p&gt;查找，也就是获取一个类型中的字段、属性、方法等的时候使用的。&lt;/p&gt;

&lt;p&gt;拿到所有成员：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NonPublic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上 &lt;a href=&quot;https://source.dot.net/#System.Runtime/System/Reflection/RuntimeReflectionExtensions.cs,a63775278f260201,references&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RuntimeReflectionExtensions.Everything&lt;/code&gt;&lt;/a&gt; 属性就是这么写的。&lt;/p&gt;

&lt;p&gt;拿到公有的实例成员：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;附-bindingflags-的源码&quot;&gt;附 BindingFlags 的源码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// NOTES: We have lookup masks defined in RuntimeType and Activator.  If we&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//    change the lookup values then these masks may need to change also.&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// a place holder for no flag specifed&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// These flags indicate what to search for when binding&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IgnoreCase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// Ignore the case of Names while searching&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DeclaredOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// Only look at the members declared on the Type&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// Include Instance members in search&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;// Include Static members in search&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;// Include Public members in search&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NonPublic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;// Include Non-Public members in search&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FlattenHierarchy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// Rollup the statics into the class.&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// These flags are used by InvokeMember to determine&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// what type of member we are trying to Invoke.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// BindingAccess = 0xFF00;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;InvokeMethod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CreateInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GetField&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SetField&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GetProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// These flags are also used by InvokeMember but they should only&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// be used when calling InvokeMember on a COM object.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PutDispProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PutRefDispProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x8000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ExactBinding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x010000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// Bind with Exact Type matching, No Change type&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SuppressChangeType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x020000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// DefaultValueBinding will return the set of methods having ArgCount or &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//    more parameters.  This is used for default values, etc.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;OptionalParamBinding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x040000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// These are a couple of misc attributes used&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IgnoreReturn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x01000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// This is used in COM Interop&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DoNotWrapExceptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x02000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Disables wrapping exceptions in TargetInvocationException&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/Reflection/BindingFlags.cs&quot;&gt;BindingFlags.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Runtime/System/Reflection/RuntimeReflectionExtensions.cs&quot;&gt;RuntimeReflectionExtensions.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 10 Mar 2019 11:05:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/binding-flags-of-reflection.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/binding-flags-of-reflection.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何使用 MyGet 这个激进的 NuGet 源体验日构建版本的 .NET Standard / .NET Core</title>
        <description>&lt;p&gt;很多库都会在 nuget.org 上发布预览版本，不过一般来说这个预览版本也是大多可用的。然而想要体验日构建版本，这个就没有了，毕竟要照顾绝大多数开发者嘛……&lt;/p&gt;

&lt;p&gt;本文介绍如何使用 MyGet 这个激进的 NuGet 源，介绍如何使用框架级别的库的预览版本如 .NET Standard 的预览版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;加入-myget-这个-nuget-源&quot;&gt;加入 MyGet 这个 NuGet 源&lt;/h2&gt;

&lt;p&gt;添加 NuGet 源的方法在我和林德熙的博客中都有说明：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/add-custom-nuget-source&quot;&gt;全局或为单独的项目添加自定义的 NuGet 源 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/VisualStudio-%E7%BB%99%E9%A1%B9%E7%9B%AE%E6%B7%BB%E5%8A%A0%E7%89%B9%E6%AE%8A%E7%9A%84-Nuget-%E7%9A%84%E9%93%BE%E6%8E%A5.html&quot;&gt;VisualStudio 给项目添加特殊的 Nuget 的链接 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;简单点，就是在 Visual Studio 中打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGet 包管理器&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;包源&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-11-58-37.png&quot; alt=&quot;管理包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后把 MyGet 的源添加进去：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&gt;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你想添加其他的 NuGet 源，可以参见我的另一篇博客：&lt;a href=&quot;/post/public-nuget-sources&quot;&gt;我收集的各种公有 NuGet 源 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用-net-standard-的预览版本&quot;&gt;使用 .NET Standard 的预览版本&lt;/h2&gt;

&lt;p&gt;因为我们在使用 .NET Standard 库的时候，是直接作为目标框架来选择的，就像下面的项目文件内容一样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，如果你直接把 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 中的值改为预览版本，是无法使用的。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 的匹配是按照字符串来匹配的，并不会解析成库和版本号。关于这一点可以如何得知的，可以参考我的另一篇博客（中英双语）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/read-microsoft-net-sdk-en&quot;&gt;Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而实际上的使用方法很简单，就是直接用正常的方法安装对应的 NuGet 包：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Install-Package NETStandard.Library -Version 2.1.0-preview1-27119-01
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者直接去 csproj 中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NETStandard.Library&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.1.0-preview1-27119-01&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于版本号如何确定，请直接前往 MyGet 网站查看：&lt;a href=&quot;https://dotnet.myget.org/feed/dotnet-core/package/nuget/NETStandard.Library&quot;&gt;dotnet-core - NETStandard.Library - MyGet&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这个时候，.NET Standard 的预览版标准库会使用以替换 .NET Standard 2.0 的正式版本库。&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Mar 2019 08:57:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/develop-with-very-early-framework-version-using-myget.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/develop-with-very-early-framework-version-using-myget.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>常用输入法快速输入自定义格式的时间和日期（搜狗/QQ/手心/微软拼音）</title>
        <description>&lt;p&gt;几个主流的输入法输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;rq&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;sj&lt;/code&gt; 都可以得到预定义格式的日期或者时间。然而他们都是预定义的格式；当我们需要一些其他格式的时候该怎么做呢？&lt;/p&gt;

&lt;p&gt;本文将介绍几个常用输入法自定义时间和日期格式的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;主流输入法的日期格式一般是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-49-17.png&quot; alt=&quot;微软拼音&quot; /&gt;&lt;br /&gt;
▲ 微软拼音&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-48-07.png&quot; alt=&quot;搜狗拼音&quot; /&gt;&lt;br /&gt;
▲ 搜狗拼音&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-51-53.png&quot; alt=&quot;QQ 拼音&quot; /&gt;&lt;br /&gt;
▲ QQ 拼音&lt;/p&gt;

&lt;p&gt;如果自定义，可以是这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-52-50.png&quot; alt=&quot;UTC 自定义&quot; /&gt;&lt;br /&gt;
▲ UTC 自定义&lt;/p&gt;

&lt;p&gt;输出效果像这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2018-08-26 15:58:05
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;微软拼音输入法&quot;&gt;微软拼音输入法&lt;/h2&gt;

&lt;p&gt;微软拼音输入法自定义短语的方法请前往：&lt;a href=&quot;/ime/2017/09/18/date-time-format-using-microsoft-pinyin.html&quot;&gt;用微软拼音快速输入自定义格式的时间和日期&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;具体的自定义字符串是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;%yyyy%-%MM%-%dd% %HH%:%mm%:%ss%
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更多自定义请参阅：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/custom-date-and-time-format-strings?wt.mc_id=MVP&quot;&gt;自定义日期和时间格式字符串 - Microsoft Docs&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;搜狗拼音输入法&quot;&gt;搜狗拼音输入法&lt;/h2&gt;

&lt;p&gt;搜狗输入法的自定义短语入口在这里：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-59-22.png&quot; alt=&quot;搜狗输入法自定义短语&quot; /&gt;&lt;br /&gt;
▲ 搜狗输入法自定义短语&lt;/p&gt;

&lt;p&gt;具体的自定义字符串是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#$year-$month_mm-$day_dd $fullhour:$minute:$second
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 注意前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; 是必须保留的，否则输入法不会将字符串进行转义&lt;/p&gt;

&lt;p&gt;字符串中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;$month&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;$day&lt;/code&gt; 后面跟着 &lt;code class=&quot;highlighter-rouge&quot;&gt;mm&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dd&lt;/code&gt;，这跟微软拼音的思路是类似的，代表具体的格式。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$year&lt;/code&gt; &lt;em&gt;2018&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$year_yy&lt;/code&gt; &lt;em&gt;18&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$year_cn&lt;/code&gt; &lt;em&gt;二零一八&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$year_yy_cn&lt;/code&gt; &lt;em&gt;一八&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$month&lt;/code&gt; &lt;em&gt;8&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$month_mm&lt;/code&gt; &lt;em&gt;08&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$month_cn&lt;/code&gt; &lt;em&gt;八&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$day&lt;/code&gt; &lt;em&gt;6&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$day_dd&lt;/code&gt; &lt;em&gt;06&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$day_cn&lt;/code&gt; &lt;em&gt;六&lt;/em&gt;, &lt;em&gt;二十六&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$weekday&lt;/code&gt; &lt;em&gt;0&lt;/em&gt;, &lt;em&gt;1&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$weekday_cn&lt;/code&gt; &lt;em&gt;日&lt;/em&gt;, &lt;em&gt;一&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$fullhour&lt;/code&gt; &lt;em&gt;15&lt;/em&gt; (24 小时制)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$halfhour&lt;/code&gt; &lt;em&gt;3&lt;/em&gt; (12 小时制)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$fullhour_cn&lt;/code&gt; &lt;em&gt;十五&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$halfhour_cn&lt;/code&gt; &lt;em&gt;三&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$ampm&lt;/code&gt; &lt;em&gt;AM&lt;/em&gt;, &lt;em&gt;PM&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$ampm_cn&lt;/code&gt; &lt;em&gt;上午&lt;/em&gt;, &lt;em&gt;下午&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$minute&lt;/code&gt; &lt;em&gt;44&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$minute_cn&lt;/code&gt; &lt;em&gt;四十四&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$second&lt;/code&gt; &lt;em&gt;40&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$second_cn&lt;/code&gt; &lt;em&gt;四十&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;qq-拼音输入法&quot;&gt;QQ 拼音输入法&lt;/h2&gt;

&lt;p&gt;QQ 拼音输入法的自定义短语入口在这里：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-16-16-37.png&quot; alt=&quot;QQ 输入法自定义短语&quot; /&gt;&lt;br /&gt;
▲ QQ 输入法自定义短语&lt;/p&gt;

&lt;p&gt;具体的自定义字符串是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(Year)-$(month)-$(date) $(hour):$(minute):$(second)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;手心输入法&quot;&gt;手心输入法&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-28-13-09-44.png&quot; alt=&quot;手心输入法自定义短语&quot; /&gt;&lt;br /&gt;
▲ 手心输入法自定义短语&lt;/p&gt;

&lt;p&gt;手心输入法的自定义字符串是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#$(year)-$(month_mm)-$(day_dd) $(fullhour):$(minute):$(second) +0800
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，前面必须有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt;，这表示后面的字符串中包含函数，需要计算函数值。如果没有前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt;，那么输入的所有内容将被视为普通字符串。&lt;/p&gt;

&lt;p&gt;实际上，你如果将手心输入法默认的自定义短语导出，你就能看到它们对自定义短语的说明还是非常齐全的：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;;手心输入法自定义短语说明
;1、自定义短语可用来快速输入日期、时间、手机号、邮箱、账号、小短文等，可由用户自行添加和修改
;2、通过设置添加的自定义短语最多支持1000条，如果有需求，其他的自定义短语可以通过导入个性短语来实现，导入个性短语支持最大50MB和最多一百万条个性短语的文本文件，当文本大小超过50MB不进行导入，当文本中词条数超过一百万条时，只录入前一百万条数据。
;3、导入的自定义短语可支持的格式有如下几种：
;    1）输入串+英文逗号+数字（指定排序位置）=短语内容
;    2）输入串=短语内容（位置默认为第1位）
;    3）输入串=数字（指定排序位置）+英文逗号+短语内容
;    4）输入串,数字（指定排序位置）=回车
;       多行语句1
;       多行语句2
;       多行语句3
;4、支持集合，可用来支持多个字符：
;    格式为：输入串=#[]，括号内为想支持的字符集合，如：
;    dxlm=#[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ] 可从第一位开始显示从1到12的罗马数字
;5、支持的时间函数：
;    格式为：输入串=#+函数或者文字，如：
;    now=3,#$(YYYY)年$(MM)月$(DD)日 $(hh):$(mm):$(ss)
;    函数(区分大小写)        说明                        例子
;    $(year)                 4位数的年份                 2006、2015
;    $(year_yy)              2位数的年份                 06、15
;    $(month_mm)             2位数的月份                 01、09、12
;    $(month)                不补零的月份                1、9、12
;    $(day_dd)               2位数的日                   01、07、24
;    $(day)                  不补零的日                  1、7、24
;    $(fullhour)             2位数的小时(24小时)         02、09、14
;    $(halfhour)             2位数的小时(12小时)         02、09
;    $(ampm)                 按当前时间显示AM或者PM      AM、PM
;    $(minute)               2位数的分钟                 08、37
;    $(second)               2位数的秒                   01、59
;    $(year_cn)              4位中文年份                 二〇一五、二〇〇八
;    $(year_yy_cn)           2位中文年份                 一五、〇八
;    $(month_cn)             中文月份                    一、九、十一
;    $(day_cn)               中文日                      八、二十四
;    $(week_cn)              中文星期                    星期一、星期三、星期天
;    $(year_ln)              农历年份                    乙未年、甲午年
;    $(month_ln)             农历月份                    正月、五月、腊月、冬月
;    $(day_ln)               农历日                      初一、初七、十三、廿四
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 07:30:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/ime-date-time-format.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/ime-date-time-format.html</guid>
        
        
        <category>ime</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>使用一句 git 命令将仓库的改动推送到所有的远端</title>
        <description>&lt;p&gt;git 支持一个本地仓库包含多个远端（remote），这对于开源社区来说是一个很重要的功能，可以实时获取到最新的开源代码且能推送到自己的仓库中提交 pull request。&lt;/p&gt;

&lt;p&gt;有时候多个远端都是自己的，典型的就是 GitHub Pages 服务了，推送总是希望这几个远端能够始终和本地仓库保持一致。本文将介绍一个命令推送到所有远端的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我的博客同时发布在 GitHub 仓库 &lt;a href=&quot;https://github.com/walterlv/walterlv.github.io&quot;&gt;https://github.com/walterlv/walterlv.github.io&lt;/a&gt; 和 Gitee 仓库 &lt;a href=&quot;http://gitee.com/walterlv/walterlv&quot;&gt;http://gitee.com/walterlv/walterlv&lt;/a&gt;。由于这两个远端的 Pages 服务没有打通，所以我总是需要同时将博客推送到两个不同的远端中。&lt;/p&gt;

&lt;h2 id=&quot;第一步设置多个远端remote&quot;&gt;第一步：设置多个远端（remote）&lt;/h2&gt;

&lt;p&gt;使用你平常使用的方法添加多个 git 远端。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git remote add github https://github.com/walterlv/walterlv.github.io.git &lt;span class=&quot;nt&quot;&gt;--no-tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意，对于不是 origin 的远端，建议不要拉取 tags，所以我加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;--no-tags&lt;/code&gt; 选项。&lt;/p&gt;

&lt;p&gt;我添加了两个新的远端（github 和 gitee）之后，打开你仓库 .git 文件夹中的 config 文件，应该可以看到如下的内容：&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[remote &quot;origin&quot;]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/walterlv/walterlv.github.io.git&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;+refs/heads/*:refs/remotes/origin/*&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;[branch &quot;master&quot;]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;remote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;origin&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;merge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;refs/heads/master&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;[remote &quot;github&quot;]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/walterlv/walterlv.github.io.git&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;+refs/heads/*:refs/remotes/github/*&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;tagopt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--no-tags&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;[remote &quot;gitee&quot;]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://gitee.com/walterlv/walterlv.git&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;+refs/heads/*:refs/remotes/gitee/*&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;tagopt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--no-tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第二步添加一个名为-all-的新远端&quot;&gt;第二步：添加一个名为 all 的新远端&lt;/h2&gt;

&lt;p&gt;现在，我们要添加一个名为 all 的新远端，并且在里面添加两个 url。由于这个步骤没有 git 命令行的帮助，所以你需要手工修改 config 文件中的内容。&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[remote &quot;all&quot;]&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/walterlv/walterlv.github.io.git&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://gitee.com/walterlv/walterlv.git&lt;/span&gt;
	&lt;span class=&quot;py&quot;&gt;tagopt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--no-tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你有更多需要同步的远端，那么就在里面添加更多的 url。&lt;/p&gt;

&lt;h2 id=&quot;开始使用一个命令同步所有的仓库&quot;&gt;开始使用一个命令同步所有的仓库&lt;/h2&gt;

&lt;p&gt;现在，你可以使用一句命令将本地的修改推送到所有的远端了。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我现在自己的博客仓库就是这样的推送方式。于是你可以在以下多个地址打开阅读我的博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://walterlv.com/&quot;&gt;https://walterlv.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/&quot;&gt;https://blog.walterlv.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://walterlv.github.io/&quot;&gt;https://walterlv.github.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://walterlv.gitee.io/&quot;&gt;https://walterlv.gitee.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://walterlv.oschina.io/&quot;&gt;https://walterlv.oschina.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:12:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-push-to-all-remotes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-push-to-all-remotes.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET/C# 获取一个正在运行的进程的命令行参数</title>
        <description>&lt;p&gt;在自己的进程内部，我们可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数传入的参数，也可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.GetCommandLineArgs&lt;/code&gt; 来获取命令行参数。&lt;/p&gt;

&lt;p&gt;但是，可以通过什么方式来获取另一个运行着的程序的命令行参数呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;进程内部获取传入参数的方法，可以参见我的另一篇博客：&lt;a href=&quot;/post/when-will-the-command-line-args-contain-the-executable-path&quot;&gt;.NET 命令行参数包含应用程序路径吗？&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;.NET Framework / .NET Core 框架内部是不包含获取其他进程命令行参数的方法的，但是我们可以在任务管理器中看到，说明肯定存在这样的方法。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-19-21-04-41.png&quot; alt=&quot;任务管理器中的命令行参数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;实际上方法是有的，不过这个方法是 Windows 上的专属方法。&lt;/p&gt;

&lt;p&gt;对于 .NET Framework，需要引用程序集 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Management&lt;/code&gt;；对于 .NET Core 需要引用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Windows.Compatibility&lt;/code&gt; 这个针对 Windows 系统准备的兼容包（不过这个兼容包目前还是预览版本）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFramework) == 'netcoreapp2.1'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Windows.Compatibility&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.1.0-preview.19073.11&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFramework) == 'net472'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Management&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ManagementObjectSearcher&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ManagementBaseObject&lt;/code&gt; 来获取命令行参数。&lt;/p&gt;

&lt;p&gt;为了简便，我将其封装成一个扩展方法，其中包括对于一些异常的简单处理。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Management&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 为 &amp;lt;see cref=&quot;Process&quot;/&amp;gt; 类型提供扩展方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProcessExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个正在运行的进程的命令行参数。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 与 &amp;lt;see cref=&quot;Environment.GetCommandLineArgs&quot;/&amp;gt; 一样，使用此方法获取的参数是包含应用程序路径的。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 关于 &amp;lt;see cref=&quot;Environment.GetCommandLineArgs&quot;/&amp;gt; 可参见：&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// .NET 命令行参数包含应用程序路径吗？https://blog.walterlv.com/post/when-will-the-command-line-args-contain-the-executable-path.html&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;process&quot;&amp;gt;一个正在运行的进程。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;表示应用程序运行命令行参数的字符串。&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCommandLineArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCommandLineArgsCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Win32Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ErrorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x80004005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 没有对该进程的安全访问权限。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 进程已退出。&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCommandLineArgsCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ManagementObjectSearcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;SELECT CommandLine FROM Win32_Process WHERE ProcessId = &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@object&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManagementBaseObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SingleOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommandLine&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用此方法得到的命令行参数是一个字符串，而不是我们通常使用字符串时的字符串数组。如果你需要将其转换为字符串数组，可以使用我在另一篇博客中使用的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/convert-command-line-string-to-args-array&quot;&gt;.NET/C# 将一个命令行参数字符串转换为命令行参数数组 args&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2633674/6233938&quot;&gt;Can I get command line arguments of other processes from .NET/C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/17582576/6233938&quot;&gt;How to get Command Line info for a process in PowerShell or C# - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:12:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-command-line-for-a-running-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-command-line-for-a-running-process.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>WPF 让普通 CLR 属性支持 XAML 绑定（非依赖属性），这样 MarkupExtension 中定义的属性也能使用绑定了</title>
        <description>&lt;p&gt;如果你写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 在 XAML 当中使用，你会发现你在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 中定时的属性是无法使用 XAML 绑定的，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 不是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;本文将给出解决方案，让你能够在任意的类型中写出支持 XAML 绑定的属性；而不一定要依赖对象（&lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject&lt;/code&gt;）和依赖属性（&lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty&lt;/code&gt;）。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;下面是一个很简单的 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt;，用户设置了什么值，就返回什么值。拿这么简单的类型只是为了避免额外引入复杂的理解难度。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvExtension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupExtension&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProvideValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以在 XAML 中直接赋值：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{local:Walterlv Value=blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但不能绑定：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SourceTextBox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{local:Walterlv Value={Binding Text, Source={x:Reference SourceTextBox}}}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为运行时会报错，提示绑定必须被设置到依赖对象的依赖属性中。在设计器中也可以看到提示不能绑定。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-01-00-02-34.png&quot; alt=&quot;运行时报错&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-01-00-09-00.png&quot; alt=&quot;设计器的警告&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;实际上这个问题是能够解决的（不过也花了我一些时间思考解决方案）。&lt;/p&gt;

&lt;p&gt;既然绑定需要一个依赖属性，那么我们就定义一个依赖属性。非依赖对象中不能定义依赖属性，于是我们定义附加属性。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注意：这一段代码实际上是无效的。&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;???.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;???.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里问题来了，获取和设置附加属性是需要一个依赖对象的，那么我们哪里去找依赖对象呢？直接定义一个新的就好了。&lt;/p&gt;

&lt;p&gt;于是我们定义一个新的依赖对象：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注意：这一段代码实际上是无效的。&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dependencyObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在虽然可以编译通过，但是我们会遇到两个问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueProperty&lt;/code&gt; 的变更通知的回调函数中，我们只能找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dependencyObject&lt;/code&gt; 的实例，而无法找到外面的类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvExtension&lt;/code&gt; 的实例；这几乎使得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Value&lt;/code&gt; 的变更通知完全失效。&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Value&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;set&lt;/code&gt; 方法中得到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 值是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 对象，而不是正常依赖属性中得到的绑定的结果；这意味着我们无法直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Value&lt;/code&gt; 的值。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了解决这两个问题，我必须自己写一个代理的依赖对象，用于帮助做属性的变更通知，以及处理绑定产生的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 对象。在正常的依赖对象和依赖属性中，这些本来都不需要我们自己来处理。&lt;/p&gt;

&lt;h2 id=&quot;方案&quot;&gt;方案&lt;/h2&gt;

&lt;p&gt;于是我写了一个代理的依赖对象，我把它命名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClrBindingExchanger&lt;/code&gt;，意思是将 CLR 属性和依赖属性的绑定进行交换。&lt;/p&gt;

&lt;p&gt;代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClrBindingExchanger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_valueChangeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClrBindingExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valueChangeCallback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_attachedProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_valueChangeCallback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valueChangeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Binding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BindingOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_attachedProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValueChangeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClrBindingExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_valueChangeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的意思是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;构造函数中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;owner&lt;/code&gt; 参数完全没有用，我只是拿来备用，你可以删掉。&lt;/li&gt;
  &lt;li&gt;构造函数中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;attachedProperty&lt;/code&gt; 参数是需要定义的附加属性。
    &lt;ul&gt;
      &lt;li&gt;因为前面我们说过，有一个附加属性才可以编译通过，所以附加属性是一定要定义的&lt;/li&gt;
      &lt;li&gt;既然一定要定义附加属性，那么就可以用起来，接下来会用&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;构造函数中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;valueChangeCallback&lt;/code&gt; 参数是为了指定变更通知的，因为前面我们说变更通知不好做，于是就这样代理做变更通知。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 这两个方法是用来代替 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject&lt;/code&gt; 自带的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 的，目的是执行我们希望特别执行的方法。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 中我们需要自己考虑绑定对象，如果发现是绑定，那么就真的进行一次绑定。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueChangeCallback&lt;/code&gt; 是给附加属性用的，因为用我的这种方法定义附加属性时，只能写出相同的代码，所以干脆就提取出来。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而用法是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvExtension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupExtension&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_valueExchanger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClrBindingExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnValueChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClrBindingExchanger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_valueExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClrBindingExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueChangeCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_valueExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_valueExchanger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnValueChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在这里可以处理 Value 属性值改变的变更通知。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProvideValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于一个属性来说，代码确实多了些，这实在是让人难受。可是，这可以达成目的呀！&lt;/p&gt;

&lt;p&gt;解释一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;定义一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_valueExchanger&lt;/code&gt;，就是在使用我们刚刚写的那个新类。&lt;/li&gt;
  &lt;li&gt;在构造函数中对 &lt;code class=&quot;highlighter-rouge&quot;&gt;_valueExchanger&lt;/code&gt; 进行初始化，因为要传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 和一个实例方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnValueChanged&lt;/code&gt;，所以只能在构造函数中初始化。&lt;/li&gt;
  &lt;li&gt;定义一个附加属性（前面我们说了，一定要有依赖属性才可以编译通过哦）。
    &lt;ul&gt;
      &lt;li&gt;注意属性的变更通知方法，需要固定写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClrBindingExchanger.ValueChangeCallback&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;定义普通的 CLR 属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;Value&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt; 方法要换成我们自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt; 哦&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 方法也要换成我们自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt; 哦，这样绑定才可以生效&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OnValueChanged&lt;/code&gt; 就是我们实际的变更通知，这里得到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;oldValue&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;newValue&lt;/code&gt; 就是你期望的值，而不是我面前面奇怪的绑定实例。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是，绑定就这么在一个普通的类型和一个普通的 CLR 属性中生效了，而且还获得了变更通知。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本文没有任何参考资料，所有方法都是我（walterlv）的原创方法，因为真的找不到资料呀！不过在找资料的过程中发现了一些没解决的文档或帖子：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/97e9f8e4-9eae-45ff-aac3-9f0c25865b14/how-to-use-clr-property-as-binding-target?forum=wpf&quot;&gt;How to use CLR property as binding target?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.c-sharpcorner.com/uploadfile/anku123/clr-object-binding-in-wpf/&quot;&gt;CLR Object Binding In WPF&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/10328974/6233938&quot;&gt;wpf - MarkupExtension with binding parameters - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/16287829/6233938&quot;&gt;c# - Binding to dependency and regular properties in WPF - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/18246316/6233938&quot;&gt;c# - XAML bind to DependencyProperty instance held in a CLR property - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blogs.profitbase.com/tsenn/?p=73&quot;&gt;Tore Senneseth’s blog » Custom Markup Extension with bindable properties&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/xaml-services/markup-extensions-for-xaml-overview&quot;&gt;Markup Extensions for XAML Overview - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/xaml-services/service-contexts-available-to-type-converters-and-markup-extensions&quot;&gt;Service Contexts Available to Type Converters and Markup Extensions - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:12:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-wpf-xaml-binding-support-for-clr-property.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-wpf-xaml-binding-support-for-clr-property.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试</title>
        <description>&lt;p&gt;在 WPF 开发时，有 Snoop 的帮助，UI 的调试将变得非常轻松。使用 Snoop，能轻松地查看 WPF 中控件的可视化树以及每一个 Visual 节点的各种属性，或者查看数据上下文，或者监听查看事件的引发。&lt;/p&gt;

&lt;p&gt;不过，更强大的是支持使用 PowerShell 脚本。这使得它即便 UI 没有给你提供一些入口，你也能通过各种方式查看或者修改 UI。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;snoop-powershell-入口&quot;&gt;Snoop PowerShell 入口&lt;/h2&gt;

&lt;p&gt;常规 Snoop 的使用方法，将狮子瞄准镜拖出来对准要调试 UI 的 WPF 窗口松开。这里我拿 Visual Studio 2019 的窗口做试验。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-21-02.png&quot; alt=&quot;调试 Visual Studio 2019 的 UI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在打开的新的 Snoop 窗口中我们打开 PowerShell 标签。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-22-54.png&quot; alt=&quot;打开 PowerShell 标签&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本文的内容将从这里开始。&lt;/p&gt;

&lt;h2 id=&quot;自带的-powershell-变量&quot;&gt;自带的 PowerShell 变量&lt;/h2&gt;

&lt;p&gt;在 Snoop 的 PowerShell 提示窗口中，我们可以得知有两个变量可以使用：&lt;code class=&quot;highlighter-rouge&quot;&gt;$root&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;$selected&lt;/code&gt;。包含这两个，还有其他的可以使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$root&lt;/code&gt; 拿到当前 Snoop 窗口顶层元素类型的实例&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$selected&lt;/code&gt; 拿到当前 Snoop 用鼠标或键盘选中的元素的实例&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$parent&lt;/code&gt; 拿到当前 Snoop 选中元素的可视化树父级&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$null&lt;/code&gt; 就是 .NET 中的 null&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，你也可以定义和使用其他的变量，后面会说。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-25-49.png&quot; alt=&quot;`$root`&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-28-19.png&quot; alt=&quot;`$selected`&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;基本的-powershell-命令&quot;&gt;基本的 PowerShell 命令&lt;/h2&gt;

&lt;h3 id=&quot;属性&quot;&gt;属性&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 获取属性&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$selected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 将属性设置为 null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$selected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;直接像 C# 语法那样一直在后面使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; 可以访问实例中的属性。不需要关心实例是什么类型的，只要拥有那个属性，就可以访问到。&lt;/p&gt;

&lt;p&gt;比如下面，上面的例子我们选中的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt;，于是我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$selected.Visual.Content&lt;/code&gt; 访问到 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Content&lt;/code&gt; 属性，而后面 &lt;code class=&quot;highlighter-rouge&quot;&gt;$selected.Visual.Content = $null&lt;/code&gt; 则是将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的内容清空了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-42-44.png&quot; alt=&quot;获取 Content 属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-snoop-powershell-content-to-null.gif&quot; alt=&quot;设置 Content 属性&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;创建对象&quot;&gt;创建对象&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 创建对象&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$button&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;System.Windows.Controls.Button&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-property&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;欢迎访问 blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-53-34.png&quot; alt=&quot;创建一个 Button&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;调用方法&quot;&gt;调用方法&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$selected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;顶部的那个按钮就是通过上面的命令添加上去的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-55-42.png&quot; alt=&quot;调用实例方法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;调用静态方法用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;[类名]::方法名(参数)&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;System.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Version.ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; running for blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-28-22-59-55.png&quot; alt=&quot;调用静态方法&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.scottlogic.com/2013/12/18/wpf-snoop-powershell.html&quot;&gt;Snooping WPF: Tips and PowerShell tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:11:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/powershell-of-snoop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/powershell-of-snoop.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>四种方法获取可执行程序的文件路径（.NET Core / .NET Framework）</title>
        <description>&lt;p&gt;本文介绍四种不同的获取可执行程序文件路径的方法。适用于 .NET Core 以及 .NET Framework。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用程序集信息获取&quot;&gt;使用程序集信息获取&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种方式的思路是获取入口程序集所在的路径。不过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly()&lt;/code&gt; 能获取到的程序集是入口托管程序集；使用此方法会返回第一个托管程序集。&lt;/p&gt;

&lt;p&gt;只有 .NET Framework 程序的入口才是托管程序（exe）。而对于 .NET Core 程序，如果直接发布成带环境依赖声明的 dll，那么实际运行的进程是 dotnet.exe；而如果发布成自包含的 exe 程序，其主 exe 也是一个非托管的 CLR 启动器而已，并不是托管程序集。&lt;/p&gt;

&lt;p&gt;所以此方法适用条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;必须是 .NET Framework 程序（.NET Core 程序不适用）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;使用应用程序域信息获取&quot;&gt;使用应用程序域信息获取&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种方式的思路是获取当前 AppDomain 所在的文件夹。不过此方法也只是获取到文件夹而已，不包含文件名。&lt;/p&gt;

&lt;p&gt;所以此方法适用条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你不需要知道文件名，只是要一个程序所在的文件夹而已。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，此方法因为不涉及到托管和非托管程序集，所以与编译结果无关，适用于 .NET Core 和 .NET Framework 程序。&lt;/p&gt;

&lt;h2 id=&quot;使用进程信息获取&quot;&gt;使用进程信息获取&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种方式的思路是获取当前进程可执行程序的完全路径。&lt;/p&gt;

&lt;p&gt;对于 .NET Framework 程序，其 exe 就是这个路径。&lt;/p&gt;

&lt;p&gt;对于 .NET Core 程序来说：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果发布成带环境依赖声明的 dll，那么此方法获取到的可执行程序名将是 dotnet.exe，这显然不会是我们预期的行为；&lt;/li&gt;
  &lt;li&gt;如果发布成自包含的 exe，那么此方法获取到的可执行程序名就是程序自己的名称，这是期望的结果。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以此方法适用条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;适用于 .NET Framework 程序；&lt;/li&gt;
  &lt;li&gt;适用于发布成自包含的 .NET Core 程序。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;使用命令行参数获取&quot;&gt;使用命令行参数获取&lt;/h2&gt;

&lt;p&gt;我在另一篇博客中提到命令行参数中包含应用程序路径：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/when-will-the-command-line-args-contain-the-executable-path&quot;&gt;.NET 命令行参数包含应用程序路径吗？ - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是我们也可以通过命令行参数来获取到可执行程序的路径。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executablePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCommandLineArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种方法的效果和前面使用进程信息获取的效果是相同的，会获取到相同的可执行程序路径。&lt;/p&gt;

&lt;h2 id=&quot;总结靠谱的方法&quot;&gt;总结靠谱的方法&lt;/h2&gt;

&lt;p&gt;通过以上方法的说明，我们可以知道目前没有 100% 可靠的获取当前可执行程序文件路径的方法，不过可以组合多种方法达到 100% 可靠的目的。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果我们只需要获取程序所在的文件夹
    &lt;ul&gt;
      &lt;li&gt;那么请直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.CurrentDomain.SetupInformation.ApplicationBase&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果我们需要获取到可执行程序的完整路径
    &lt;ul&gt;
      &lt;li&gt;先通过进程或者命令行参数的方式获取
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetCurrentProcess().MainModule.FileName&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.GetCommandLineArgs()[0]&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果得到的进程是 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet.exe&lt;/code&gt;，那么再通过程序集信息获取
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly().Location&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;另外，关于以上方法的性能对比，你可以参阅林德熙的博客：&lt;a href=&quot;https://blog.lindexi.com/post/dotnet-%E8%8E%B7%E5%8F%96%E7%A8%8B%E5%BA%8F%E6%89%80%E5%9C%A8%E8%B7%AF%E5%BE%84%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 获取路径各种方法的性能对比&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:10:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-current-executable-file-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-current-executable-file-path.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>UWP 在 WebView 中执行 JavaScript 代码（用于模拟用户输入等）</title>
        <description>&lt;p&gt;UWP 中使用 WebView 时可以在网页中额外执行一些代码。于是你几乎可以在网页上做任何事情，那些你可以在浏览器控制台中做的事情。&lt;/p&gt;

&lt;p&gt;本文将介绍做法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备环境&quot;&gt;准备环境&lt;/h2&gt;

&lt;p&gt;在页面（XAML）中放一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebView&lt;/code&gt;，然后取个名字，比如就叫做 &lt;code class=&quot;highlighter-rouge&quot;&gt;WebView&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;监听 &lt;code class=&quot;highlighter-rouge&quot;&gt;NavigationCompleted&lt;/code&gt; 事件，然后导航到需要操作的页面。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NavigationCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnNavigationCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Navigate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnNavigationCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebView&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebViewNavigationCompletedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 我们接下来的代码都将在这里编写。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;要执行 JavaScript 代码，必须要导航完成才行，所以我们接下来的代码都是写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;NavigationCompleted&lt;/code&gt; 事件处理函数中的。&lt;/p&gt;

&lt;h2 id=&quot;执行-javascript-代码&quot;&gt;执行 JavaScript 代码&lt;/h2&gt;

&lt;h3 id=&quot;模拟用户输入&quot;&gt;模拟用户输入&lt;/h3&gt;

&lt;p&gt;下面这一句的代码是填充用户 Id 一栏：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeScriptAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;document.getElementById('userId').value = 'walterlv';&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-30-21-49-37.png&quot; alt=&quot;登录页面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;登录页面截图来自于 &lt;a href=&quot;https://codedefault.com/&quot;&gt;码友网&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;javascript-evalstring-函数&quot;&gt;JavaScript eval(string) 函数&lt;/h3&gt;

&lt;p&gt;在上面的代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;eval&lt;/code&gt; 是指执行 JavaScript 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;eval&lt;/code&gt; 函数，并且将后面的字符串数组作为它的参数传入。&lt;/p&gt;

&lt;p&gt;在 JavaScript 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;eval(string)&lt;/code&gt; 函数可计算某个字符串，并执行其中的的 JavaScript 代码。在计算结束后，会返回一个字符串，就是参数中那个字符串执行完之后的返回值（如果有的话）。&lt;/p&gt;

&lt;p&gt;于是意味着你可以通过这种方式拿到输入框中的值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeScriptAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;document.getElementById('userId').value;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行完后，可以得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;userId&lt;/code&gt; 的值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 字符串；也就是我们上一步填充的那个值。&lt;/p&gt;

&lt;h3 id=&quot;模拟用户登录&quot;&gt;模拟用户登录&lt;/h3&gt;

&lt;p&gt;完整的输入用户名、密码，并点击登录按钮的代码则是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoginWebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeScriptAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;document.getElementById('userId').value = 'walterlv';&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoginWebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeScriptAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;document.getElementById('password').value = '不想让你看见的密码';&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoginWebView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeScriptAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;document.getElementById('submit').click();&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.w3school.com.cn/js/jsref_eval.asp&quot;&gt;JavaScript eval() 函数&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E6%A8%A1%E6%8B%9F%E7%BD%91%E9%A1%B5%E8%BE%93%E5%85%A5.html&quot;&gt;win10 uwp 模拟网页输入&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:10:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/invoke-web-script-in-webview.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/invoke-web-script-in-webview.html</guid>
        
        
        <category>uwp</category>
        
        <category>javascript</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio Code 中为代码片段（Code Snippets）添加快捷键</title>
        <description>&lt;p&gt;Visual Studio Code 默认是关闭了 Markdown 的智能感知提示的（因为真的是不好用，尤其是其没有中文分词的情况下）。那么在没有智能感知提示的情况下如何快速插入代码片段呢？&lt;/p&gt;

&lt;p&gt;可以使用快捷键！&lt;/p&gt;

&lt;p&gt;本文介绍如何为代码片段绑定快捷键。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;代码片段本没有快捷键相关的字段可供设置的，不过在快捷键设置中可以添加代码片段相关的设置。&lt;/p&gt;

&lt;p&gt;首先，在 Visual Studio Code 中打开快捷键设置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-09-59-06.png&quot; alt=&quot;打开快捷键设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选择手工编辑快捷键配置文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-10-00-45.png&quot; alt=&quot;手工编辑快捷键配置文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在配置文件中添加这些代码即可关联一个代码片段：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;alt+p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;editor.action.insertSnippet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;editorTextFocus&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;langId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;markdown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Insert a post for blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这个配置中，&lt;code class=&quot;highlighter-rouge&quot;&gt;alt+p&lt;/code&gt; 是我指定的快捷键，&lt;code class=&quot;highlighter-rouge&quot;&gt;editor.action.insertSnippet&lt;/code&gt; 表示执行命令插入代码片段，生效条件为 &lt;code class=&quot;highlighter-rouge&quot;&gt;editorTextFocus&lt;/code&gt; 及文本编辑器获得焦点的期间。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt; 字段指定了两个值，作为对一个现有代码片段的引用。&lt;code class=&quot;highlighter-rouge&quot;&gt;langId&lt;/code&gt; 是生效的语言 Id，&lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; 是代码片段的名称。这个名称是我在 &lt;a href=&quot;/post/add-custom-code-snippet-for-vscode&quot;&gt;在 Visual Studio Code 中添加自定义的代码片段&lt;/a&gt; 中做的代码片段的名称。&lt;/p&gt;

&lt;p&gt;保存，现在按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;alt+p&lt;/code&gt; 后就会插入指定的代码片段了。&lt;/p&gt;

&lt;p&gt;事实上，&lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt; 也可以不是引用，而直接是代码片段的内容：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;alt+p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;editor.action.insertSnippet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;editorTextFocus&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;snippet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@[TOC](walterlv 的博客目录)&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，也不需要事先定义代码片段了。&lt;/p&gt;

&lt;p&gt;额外提及以下，Visual Studio Code 快捷键只能设置全局的而不能设置仅工作区生效，详情请看 &lt;a href=&quot;https://github.com/Microsoft/vscode/issues/10708&quot;&gt;load keybindings.json from .vscode dir if there is any ? · Issue #10708 · Microsoft/vscode&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets&quot;&gt;Creating your own snippets in Visual Studio Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/vscode/issues/10708&quot;&gt;load keybindings.json from .vscode dir if there is any ? · Issue #10708 · Microsoft/vscode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:10:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/key-binding-to-snippets-for-vscode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/key-binding-to-snippets-for-vscode.html</guid>
        
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>WindowsXamlHost：在 WPF 中使用 UWP 控件库中的控件</title>
        <description>&lt;p&gt;在 &lt;a href=&quot;/post/use-uwp-controls-in-wpf&quot;&gt;WindowsXamlHost：在 WPF 中使用 UWP 的控件（Windows Community Toolkit）&lt;/a&gt; 一文中，我们说到了在 WPF 中引入简单的 UWP 控件以及相关的注意事项。不过，通常更有实际价值的是更复杂的 UWP 控件的引入，通常是一整个 Page。&lt;/p&gt;

&lt;p&gt;本文将介绍如何在 WPF 项目中引用 UWP 的控件库。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建一个-uwp-控件库&quot;&gt;创建一个 UWP 控件库&lt;/h2&gt;

&lt;p&gt;建议专门为你复杂的 UWP 控件创建一个 UWP 控件库。在这个控件库中的开发就像普通 UWP 应用一样。这样比较容易创建出更复杂的 UWP 控件出来，而不会与 WPF 项目产生太多的影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-05-48.png&quot; alt=&quot;创建一个 UWP 控件库&quot; /&gt;&lt;br /&gt;
▲ 创建一个 UWP 控件库&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-06-45.png&quot; alt=&quot;选择 SDK 版本&quot; /&gt;&lt;br /&gt;
▲ 选择 SDK 版本&lt;/p&gt;

&lt;h2 id=&quot;对-wpf-项目的准备工作&quot;&gt;对 WPF 项目的准备工作&lt;/h2&gt;

&lt;p&gt;你依然需要阅读 &lt;a href=&quot;/post/use-uwp-controls-in-wpf&quot;&gt;WindowsXamlHost：在 WPF 中使用 UWP 的控件（Windows Community Toolkit）&lt;/a&gt; 一文，以便将你的 WPF 项目改造成可以访问 UWP 类型的项目。&lt;/p&gt;

&lt;h2 id=&quot;不方便的引入方式&quot;&gt;不方便的引入方式&lt;/h2&gt;

&lt;p&gt;你如果直接让 WPF 项目添加 UWP 项目的引用，将会得到一个错误提示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-49-27.png&quot; alt=&quot;不能引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;也就是说并不能直接完成这样的引用。&lt;/p&gt;

&lt;p&gt;也许将来 WPF 项目格式更新或者 Visual Studio 的更新能为我们带来这样更直接此引用方式。不过现在来看，还不能如此方便地使用。&lt;/p&gt;

&lt;h2 id=&quot;编辑-uwp-项目文件&quot;&gt;编辑 UWP 项目文件&lt;/h2&gt;

&lt;p&gt;是的，你需要手工编写 UWP 的项目文件。&lt;/p&gt;

&lt;p&gt;如果你阅读过 &lt;a href=&quot;/post/create-uwp-app-from-zero-0&quot;&gt;(1/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序&lt;/a&gt; 这篇文章，或者已经 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解了 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;，那么对这里 csproj 文件的编辑应该不会感觉到陌生或者害怕。当然，即便你没有编辑过或者不理解 csproj 也不用担心，你只需要按照本文要求进行操作即可。&lt;/p&gt;

&lt;p&gt;现在，右击卸载项目，再右击编辑项目文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-08-09.png&quot; alt=&quot;编辑项目文件&quot; /&gt;&lt;br /&gt;
▲ 编辑项目文件&lt;/p&gt;

&lt;p&gt;找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; targets 的哪一行，你需要在那一行前面的任意位置添加以下特别标注为新增的几行：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;PropertyGroup&amp;gt;
++    &amp;lt;EnableTypeInfoReflection&amp;gt;false&amp;lt;/EnableTypeInfoReflection&amp;gt;
++    &amp;lt;EnableXBindDiagnostics&amp;gt;false&amp;lt;/EnableXBindDiagnostics&amp;gt;
++  &amp;lt;/PropertyGroup&amp;gt;
&lt;/span&gt;    &amp;lt;Import Project=&quot;$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后，还要在以上 targets 之后再添加以下代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里需要填写你的 WPF 项目的路径 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;HostFrameworkProjectFolder&amp;gt;&lt;/span&gt;$(ProjectDir)..\Whitman.Wpf&lt;span class=&quot;nt&quot;&gt;&amp;lt;/HostFrameworkProjectFolder&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjPath&amp;gt;&lt;/span&gt;obj\$(Platform)\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjPath&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(Platform)' == 'AnyCPU' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjPath&amp;gt;&lt;/span&gt;obj\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjPath&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 把此项目的输出文件都拷贝到 WPF 项目的生成路径下 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PostBuildEvent&amp;gt;&lt;/span&gt;
    md $(HostFrameworkProjectFolder)\$(ProjectName)
    md $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName)
    copy $(TargetDir)*.xbf            $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName)
    copy $(ProjectDir)*.xaml          $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName)
    copy $(ProjectDir)*.xaml.cs       $(HostFrameworkProjectFolder)\$(ProjectName)
    copy $(ProjectDir)$(ObjPath)*.g.* $(HostFrameworkProjectFolder)\$(ProjectName)
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PostBuildEvent&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一定要在 targets 之后添加这些代码，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(TargetDir)&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;$(ProjectName)&lt;/code&gt; 等属性是在那里的 targets 执行完后才生成的。&lt;/li&gt;
  &lt;li&gt;你的 UWP 项目中需要有 xaml，比如可以添加一个 MainPage.xaml 和 MainPage.xaml.cs，不然编译的时候可能会出现错误。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;重新加载项目并编译&quot;&gt;重新加载项目并编译&lt;/h2&gt;

&lt;p&gt;现在，重新加载那个 UWP 控件库，将其编译，以便将 UWP 项目的生成文件复制到 WPF 目录下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-38-28.png&quot; alt=&quot;生成的文件已复制到 WPF 目录下&quot; /&gt;&lt;br /&gt;
▲ 生成的文件已复制到 WPF 目录下&lt;/p&gt;

&lt;h2 id=&quot;在-wpf-项目中间接引用-uwp-控件库&quot;&gt;在 WPF 项目中间接引用 UWP 控件库&lt;/h2&gt;

&lt;p&gt;现在，在 WPF 项目中开启所有文件夹的显示，然后将 UWP 项目中生成的文件添加到 WPF 项目中：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-39-36.png&quot; alt=&quot;在 WPF 项目中添加 UWP 的控件库&quot; /&gt;&lt;br /&gt;
▲ 在 WPF 的项目中添加 UWP 的控件库&lt;/p&gt;

&lt;p&gt;为了能够在每次编译 WPF 项目的时候确保 UWP 项目先编译，需要为 WPF 项目设置项目依赖。在依赖对话框中将 UWP 项目设为依赖。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-41-19.png&quot; alt=&quot;添加项目依赖&quot; /&gt;&lt;br /&gt;
▲ 添加项目依赖&lt;/p&gt;

&lt;p&gt;现在，编译 WPF 项目的时候，会将 UWP 项目编译后的源码也一起编译到 WPF 项目中；相当于间接使用了 UWP 的控件库。&lt;/p&gt;

&lt;p&gt;特别的，如果你的项目被 git 进行版本管理，你可能需要忽略 UWP 控件库项目中的文件。方法是在 WPF 项目内生成的 UWP 文件夹下添加一个 .gitignore 文件，填写所有内容忽略：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*.*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-11-59-37.png&quot; alt=&quot;忽略所有内容&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但记得需要额外通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;git add ./Whitman.Wpf/Whitman.Uwp/.gitignore&lt;/code&gt; 把这个文件添加到版本管理中，不然其他人不会生效。&lt;/p&gt;

&lt;h2 id=&quot;在-wpf-项目中使用-uwp-控件库中的控件&quot;&gt;在 WPF 项目中使用 UWP 控件库中的控件&lt;/h2&gt;

&lt;p&gt;这时，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowsXamlHost&lt;/code&gt; 中就可以添加 UWP 控件库中的 MainPage 了。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;XamlHost:WindowsXamlHost&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InitialTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Whitman.Universal.MainPage&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，你可以在局部获得 UWP 完整 Page 的支持。或者你整个界面都是用 UWP 开发都没问题，并且还能获得 .NET Framework 的完全访问支持。（当然，未来一定是 .NET Core。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-12-12-14.png&quot; alt=&quot;运行后的效果&quot; /&gt;&lt;br /&gt;
▲ 运行后的效果&lt;/p&gt;

&lt;p&gt;可以使用 UWP 的 Page，并且也能弹出 UWP 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;MessageDialog&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;而 MainPage 就是普通的 UWP MainPage：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Whitman.Universal.MainPage&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;using:Walterlv.Whitman.Universal&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ApplicationPageBackgroundThemeBrush}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;400&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;欢迎访问 吕毅的博客&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Click&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DemoButton_Click&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.UI.Popups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.UI.Xaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.UI.Xaml.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Whitman.Universal&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainPage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Page&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DemoButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MessageDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UWP 的消息框，在 WPF 的窗口中。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/wpf-winforms/windowsxamlhost?wt.mc_id=MVP&quot;&gt;WindowsXAMLHost control - Windows Community Toolkit - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-enhance#first-set-up-your-project?wt.mc_id=MVP&quot;&gt;Enhance your desktop application for Windows 10 - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:09:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-uwp-control-library-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-uwp-control-library-in-wpf.html</guid>
        
        
        <category>uwp</category>
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET 中创建支持集合初始化器的类型</title>
        <description>&lt;p&gt;对象初始化器和集合初始化器只是语法糖，但是能让你的代码看起来更加清晰。至少能让对象初始化的代码和其他业务执行的代码分开，可读性会好一些。&lt;/p&gt;

&lt;p&gt;本文将编写一个类型，可以使用集合初始化器构造这个类型。不只是添加元素的集合初始化器，还有带索引的集合初始化器。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;稍微提一下对象初始化器&quot;&gt;稍微提一下对象初始化器&lt;/h2&gt;

&lt;p&gt;很普通的类型就可以支持对象初始化器，只需要对象有可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;set&lt;/code&gt; 的属性或者可访问的字段即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;初始化时可以使用&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;基本上大家编写的类或多或少都会支持对象初始化器，所以本文不会对此谈论更多的内容。&lt;/p&gt;

&lt;h2 id=&quot;通常的集合初始化器&quot;&gt;通常的集合初始化器&lt;/h2&gt;

&lt;p&gt;当你定义一个集合的时候，你会发现你的类型已经天然支持集合初始化器了。比如你定义了下面这个集合：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略集合定义的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么此集合初始化的代码就可以写成下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上你会发现实现一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ICollection&lt;/code&gt; 是一件非常繁琐的事情。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-08-15-56-50.png&quot; alt=&quot;实现一个 ICollection 需要实现的方法&quot; /&gt;&lt;br /&gt;
▲ 实现一个 ICollection 需要实现的方法&lt;/p&gt;

&lt;h2 id=&quot;最简单的集合初始化器&quot;&gt;最简单的集合初始化器&lt;/h2&gt;

&lt;p&gt;只是做一个集合初始化器的话并不需要写上面那么多的代码。&lt;/p&gt;

&lt;p&gt;实际上，你只需要两个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;实现 IEnumerable 接口或任何子接口&lt;/li&gt;
  &lt;li&gt;有一个 Add 方法&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;就像这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()=&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是你就可以像一个一般的集合那样去使用集合初始化器了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.blog.csdn.net/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;多个参数的集合初始化器&quot;&gt;多个参数的集合初始化器&lt;/h2&gt;

&lt;p&gt;刚刚我们的例子中 Add 方法只有一个参数，实际上也可以是多个参数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()=&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;includeProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在初始化的方法就有点像字典了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.blog.csdn.net/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然你也可以写更多参数，看起来更加丧心病狂。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()=&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;includeProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.blog.csdn.net/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;带索引集合初始化器&quot;&gt;带索引集合初始化器&lt;/h2&gt;

&lt;p&gt;如果你期望的初始化方法是索引，实际上也不需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 方法。只需要增加一个索引的定义即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()=&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 请忽略这里的 Bug，这只是一个语法糖的示例。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Site&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，可以使用索引方式的集合初始化器：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;吕毅&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;林德熙&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://blog.lindexi.com/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;这是一个可以发挥创造力的语法糖&quot;&gt;这是一个可以发挥创造力的语法糖&lt;/h2&gt;

&lt;p&gt;利用单个和多个参数的集合初始化器，以及带索引的集合初始化器，我们甚至可以用集合初始化器去构造一些看起来不像集合的类型。这又是一波语法糖！&lt;/p&gt;

&lt;p&gt;当然有一点值得注意，使用集合初始化器初始化的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;this[]&lt;/code&gt; 的初始化是不能同时使用的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;事实上微软的官方文档中并没有对集合初始化器的最简实现有多少描述，所以以下的参考实际上并没有用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;英文：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers?wt.mc_id=MVP&quot;&gt;Object and Collection Initializers (C# Programming Guide) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;中文：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers?wt.mc_id=MVP&quot;&gt;对象和集合初始值设定项（C# 编程指南） - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:09:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-class-that-supports-collection-initializer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-class-that-supports-collection-initializer.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 多线程 UI：设计一个异步加载 UI 的容器</title>
        <description>&lt;p&gt;对于 WPF 程序，如果你有某一个 UI 控件非常复杂，很有可能会卡住主 UI，给用户软件很卡的感受。但如果此时能有一个加载动画，那么就不会感受到那么卡顿了。UI 的卡住不同于 IO 操作或者密集的 CPU 计算，WPF 中的 UI 卡顿时，我们几乎没有可以让 UI 响应的方式，因为 WPF 一个窗口只有一个 UI 线程。&lt;/p&gt;

&lt;p&gt;No！WPF 一个窗口可以不止一个 UI 线程，本文将设计一个异步加载 UI 的容器，可以在主线程完全卡死的情况下显示一个加载动画。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是对我另一篇博客 &lt;a href=&quot;/post/multi-thread-ui-using-visualtarget-in-wpf&quot;&gt;WPF 同一窗口内的多线程 UI（VisualTarget）&lt;/a&gt; 的一项应用。阅读本文，你将得到一个 UI 控件 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBox&lt;/code&gt;，放入其中的控件即便卡住主线程，也依然会有一个加载动画缓解用户的焦虑情绪。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;异步加载的效果预览&quot;&gt;异步加载的效果预览&lt;/h2&gt;

&lt;p&gt;下图的黑屏部分是正在加载一个布局需要花 500ms 的按钮。我们可以看到，即便是主线程被占用了 500ms，依然能有一个加载动画缓解用户的等待焦虑。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-08-loading-view-preview.gif&quot; alt=&quot;异步加载效果预览&quot; /&gt;&lt;br /&gt;
▲ 异步加载效果预览&lt;/p&gt;

&lt;h2 id=&quot;使用我写的-wpf-异步加载控件-asyncbox&quot;&gt;使用我写的 WPF 异步加载控件 AsyncBox&lt;/h2&gt;

&lt;p&gt;控件的名字为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBox&lt;/code&gt;，意为异步加载显示 UI 的容器。如果要使用它，可以很简单地写出以下代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ww:AsyncBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;LoadingViewType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo:LoadingView&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;demo:LongTimeView&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ww:AsyncBox&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;LoadingView&lt;/code&gt; 是在指定用哪一个控件来做加载动画。由于这个控件会在后台线程创建并执行，为了避免意外的线程问题，这里传入类型，而不是实例。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LongTimeView&lt;/code&gt; 是一个用来模拟耗时 UI 的模拟控件。&lt;/p&gt;

&lt;p&gt;如果要看整个窗口，则是下面这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:ww=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Windows;assembly=Walterlv.Windows&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:demo=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Black&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ww:AsyncBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;LoadingViewType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo:LoadingView&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;demo:LongTimeView&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ww:AsyncBox&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LongTimeView&lt;/code&gt; 则是这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.LongTimeView&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt; 
             &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt; 
             &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; 
             &lt;span class=&quot;na&quot;&gt;d:DesignHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d:DesignWidth=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontFamily=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Monaco&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DelayButton_Click&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LongTimeView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserControl&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LongTimeView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MeasureOverride&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MeasureOverride&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DelayButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;LoadingView&lt;/code&gt; 则很简单，只是一个无限旋转的动画而已。同时它还没有后台代码：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-08-loading-view.gif&quot; alt=&quot;LoadingView 的动画效果&quot; /&gt;&lt;br /&gt;
▲ LoadingView 的动画效果&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.LoadingView&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt; 
             &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt; 
             &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
             &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d:DesignHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d:DesignWidth=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FrameworkElement.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.Loading&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Target&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(UIElement.RenderTransform).(RotateTransform.Angle)&quot;&lt;/span&gt;
                             &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1440&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1.5&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RepeatBehavior=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forever&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DoubleAnimation&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/FrameworkElement.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Target&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stroke=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;StrokeThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;8&quot;&lt;/span&gt;
                 &lt;span class=&quot;na&quot;&gt;StrokeDashArray=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;StrokeDashCap=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Round&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RenderTransformOrigin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.5 0.5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Ellipse.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;RotateTransform&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Ellipse.RenderTransform&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Ellipse.Triggers&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;EventTrigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RoutedEvent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FrameworkElement.Loaded&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BeginStoryboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Storyboard.Loading}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/EventTrigger&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Ellipse.Triggers&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Ellipse&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;现在我们来实现这个异步加载-ui-的容器&quot;&gt;现在，我们来实现这个异步加载 UI 的容器&lt;/h2&gt;

&lt;p&gt;你需要为你的项目添加以下文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-08-20-41-51.png&quot; alt=&quot;项目文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中，1、2、3、4、6 这几个文件可分别从以下链接找到并下载到你的项目中：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Annotations/Annotations.cs&quot;&gt;Annotations.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-awaiterinterfaces-cs&quot;&gt;AwaiterInterfaces.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-dispatcherasyncoperation-cs&quot;&gt;DispatcherAsyncOperation.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/walterlv/ca0fc857eae04c1088aebcb8d636d1cb#file-uidispatcher-cs&quot;&gt;UIDispatcher.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/VisualTargetPresentationSource.cs&quot;&gt;VisualTargetPresentationSource.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这些文件都是通用的异步类型。&lt;/p&gt;

&lt;p&gt;第 5 个文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBox&lt;/code&gt; 就是我们要实现的主要类型。&lt;/p&gt;

&lt;p&gt;实现思路是建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt;（类似于窗口的根 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndSource&lt;/code&gt;），这可以用来承载一个新的可视化树（Visual Tree）。这样，我们就能在一个窗口中显示两个可视化树了。&lt;/p&gt;

&lt;p&gt;这两个可视化树通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;HostVisual&lt;/code&gt; 跨线程连接起来，于是我们能在一个窗口中得到两个不同线程的可视化树。&lt;/p&gt;

&lt;p&gt;由于这两棵树不在同一个线程中，于是主线程即便卡死，也不影响后台用来播放加载动画的线程。&lt;/p&gt;

&lt;h2 id=&quot;附-asyncbox-的源码&quot;&gt;附 AsyncBox 的源码&lt;/h2&gt;

&lt;p&gt;如果你不能在下面看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBox&lt;/code&gt; 的源码，那么你的网络应该是被屏蔽了，可以访问 &lt;a href=&quot;https://gist.github.com/walterlv/4581ee10530a21ddf00f47b2cd680714&quot;&gt;AsyncBox.cs - A UI container for async loading.&lt;/a&gt; 查看。&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/4581ee10530a21ddf00f47b2cd680714.js&quot;&gt;&lt;/script&gt;

</description>
        <pubDate>Sat, 09 Mar 2019 01:09:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/design-an-async-loading-view.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/design-an-async-loading-view.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WindowsXamlHost：在 WPF 中使用 UWP 的控件（Windows Community Toolkit）</title>
        <description>&lt;p&gt;Windows Community Toolkit 再次更新到 5.0。以前可以在 WPF 中使用有限的 UWP 控件，而现在有了 WindowsXamlHost，则可以使用更多 UWP 原生控件了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;关于 Windows Community Toolkit 早期版本的 Xaml Bridge，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8-Edge-%E6%B5%8F%E8%A7%88%E5%99%A8.html&quot;&gt;WPF 使用 Edge 浏览器 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-nuget-包&quot;&gt;安装 NuGet 包&lt;/h2&gt;

&lt;p&gt;你需要做的第一步，是在你的 WPF 项目中安装 Microsoft.Toolkit.Wpf.UI.XamlHost。建议直接在 项目的 NuGet 管理器中搜索并安装。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-34-39.png&quot; alt=&quot;安装 Microsoft.Toolkit.Wpf.UI.XamlHost&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-43-24.png&quot; alt=&quot;安装好 NuGet 包后查看引用&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;配置-wpf-项目能访问-uwp-的类型&quot;&gt;配置 WPF 项目能访问 UWP 的类型&lt;/h2&gt;

&lt;p&gt;因为我们即将开始使用到 UWP 中的控件类型，所以需要配置项目能够访问到 Windows Runtime 的类型。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-56-19.png&quot; alt=&quot;添加引用&quot; /&gt;&lt;br /&gt;
▲ 添加引用&lt;/p&gt;

&lt;p&gt;你需要在你的 WPF 项目中添加以下 6 个引用才能访问 UWP 的类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5
    &lt;ul&gt;
      &lt;li&gt;引用 System.Runtime.WindowsRuntime&lt;/li&gt;
      &lt;li&gt;引用 System.Runtime.WindowsRuntime.UI.Xaml&lt;/li&gt;
      &lt;li&gt;引用 System.Runtime.InteropServices.WindowsRuntime&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade
    &lt;ul&gt;
      &lt;li&gt;引用 Windows.winmd&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;C:\Program Files (x86)\Windows Kits\10\References
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;在此目录下选择你的 SDK 版本（如 16299,17763 等）&lt;/em&gt;
        &lt;ul&gt;
          &lt;li&gt;Windows.Foundation.UniversalApiContract
            &lt;ul&gt;
              &lt;li&gt;&lt;em&gt;在此目录下选择你的 API 版本（如 4.0.0.0）&lt;/em&gt;
                &lt;ul&gt;
                  &lt;li&gt;引用 Windows.Foundation.UniversalApiContract.winmd&lt;/li&gt;
                &lt;/ul&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;Windows.Foundation.FoundationContract
            &lt;ul&gt;
              &lt;li&gt;&lt;em&gt;在此目录下选择你的 API 版本（如 3.0.0.0）&lt;/em&gt;
                &lt;ul&gt;
                  &lt;li&gt;引用 Windows.Foundation.FoundationContract.winmd&lt;/li&gt;
                &lt;/ul&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在你添加完这些引用之后，还需要选中这些引用，右击属性，把所有的 “复制到本地” 选项设置为 “否”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-10-16.png&quot; alt=&quot;不要复制到本地&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-57-03.png&quot; alt=&quot;添加 Windows Runtime 的 .NET Framework 类型引用&quot; /&gt;&lt;br /&gt;
▲ 添加 Windows Runtime 的 .NET Framework 类型引用&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-57-44.png&quot; alt=&quot;添加 Windows.WinMD 的引用&quot; /&gt;&lt;br /&gt;
▲ 添加 Windows.WinMD 的引用&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-58-07.png&quot; alt=&quot;在添加引用时注意选择 SDK 的版本号&quot; /&gt;&lt;br /&gt;
▲ 在添加引用时注意选择 SDK 的版本号&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-58-41.png&quot; alt=&quot;添加 Windows.Foundation.UniversalApiContract.winmd&quot; /&gt;&lt;br /&gt;
▲ 添加 Windows.Foundation.UniversalApiContract.winmd&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-09-58-54.png&quot; alt=&quot;添加 Windows.Foundation.FoundationContract.winmd&quot; /&gt;&lt;br /&gt;
▲ 添加 Windows.Foundation.FoundationContract.winmd&lt;/p&gt;

&lt;h2 id=&quot;开始在-wpf-中使用-uwp-的控件&quot;&gt;开始在 WPF 中使用 UWP 的控件&lt;/h2&gt;

&lt;p&gt;你可以像使用普通 WPF 控件一样将 WindowsXamlHost 添加到你的 WPF 界面中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;拖拽到界面设计器中&lt;/li&gt;
  &lt;li&gt;拖拽到 XAML 代码行中&lt;/li&gt;
  &lt;li&gt;直接在 XAML 代码中写&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-17-54.png&quot; alt=&quot;添加 WindowsXamlHost 控件&quot; /&gt;&lt;br /&gt;
▲ 添加 WindowsXamlHost 控件&lt;/p&gt;

&lt;p&gt;接着，指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;InitialTypeName&lt;/code&gt; 属性为 UWP 中的控件的名称（带命名空间）。这样，当 WindowsXamlHost 初始化的时候，也会初始化一个 UWP 的控件。&lt;/p&gt;

&lt;p&gt;这里为了简单，我初始化一个 UWP 的按钮。但必须得为 UWP 的按钮进行一些初始化，所以我监听了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ChangedChanged&lt;/code&gt; 事件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;XamlHost:WindowsXamlHost&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;InitialTypeName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Windows.UI.Xaml.Controls.Button&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ChildChanged=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowsXamlHost_ChildChanged&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowsXamlHost_ChildChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowsXamlHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Xaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UwpButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UwpButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;可以忽略的错误&quot;&gt;可以忽略的错误&lt;/h2&gt;

&lt;p&gt;在启动的时候，你可能会遇到一些异常。比如下面这个：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-33-27.png&quot; alt=&quot;没有 Application&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为我们不是原生的 UWP，而是 Host 在 WPF 中的 UWP 控件，所以会没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt;。这在 UWP 控件初始化内部已经 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 了，所以你可以忽略。&lt;/p&gt;

&lt;h2 id=&quot;最终效果&quot;&gt;最终效果&lt;/h2&gt;

&lt;p&gt;当将程序跑起来之后，你就能看到 WPF 窗口中的 UWP 控件了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-uwp-button-in-wpf-window.gif&quot; alt=&quot;运行效果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;值得注意的地方&quot;&gt;值得注意的地方&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;目前 WindowsXamlHost 还不够稳定，会出现一些闪退
    &lt;ul&gt;
      &lt;li&gt;这点就需要为 WindowsCommunityToolkit 贡献 Issues 或代码了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Host 的 UWP 控件是一个新的 HwndSource，这相当于 UWP 的控件是通过子窗口的形式与 WPF 窗口放在一起的
    &lt;ul&gt;
      &lt;li&gt;于是，只能指定一个矩形区域完全属于 UWP，在这个区域 WPF 控件无法与其获得交互或渲染叠加&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;关于-dpi-适配&quot;&gt;关于 DPI 适配&lt;/h2&gt;

&lt;p&gt;为了让 UWP 控件能够在 WPF 窗口中获得正确的 Per-Monitor 的 DPI 适配效果，你需要设置为 PerMonitorV2 的 DPI 感知级别。&lt;/p&gt;

&lt;p&gt;在 PerMonitorV2 的 DPI 感知级别下，UWP 控件能够正常获得 DPI 缩放。&lt;/p&gt;

&lt;p&gt;在 100% DPI 的屏幕下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-46-46.png&quot; alt=&quot;100% DPI 下&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 150% DPI 的屏幕下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-46-49.png&quot; alt=&quot;PerMonitorV2 感知级别 150% DPI 下&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而如果只是指定为 PerMonitor，那么切换 DPI 或者切换屏幕的时候，只有 WPF 部分会缩放，而 UWP 部分不会变化。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-04-10-48-07.png&quot; alt=&quot;PerMonitor 感知级别 150% DPI 下&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关于 PerMonitorV2 和 PerMonitor 的理解和区别，可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于如何在 WPF 下开启 PerMonitorV2 级别的 DPI 感知可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development-for-wpf&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更复杂的-uwp-控件嵌入&quot;&gt;更复杂的 UWP 控件嵌入&lt;/h2&gt;

&lt;p&gt;如果希望将更多的 WPF 窗口内的 UI 部分交给 UWP 来做，那么就不能只是仅仅初始化一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt; 就完了。&lt;/p&gt;

&lt;p&gt;你需要引入一个 UWP 控件库。阅读以下文章了解更多：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/use-uwp-control-library-in-wpf&quot;&gt;WindowsXamlHost：在 WPF 中使用 UWP 控件库中的控件 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/wpf-winforms/windowsxamlhost?wt.mc_id=MVP&quot;&gt;WindowsXAMLHost control - Windows Community Toolkit - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-enhance#first-set-up-your-project?wt.mc_id=MVP&quot;&gt;Enhance your desktop application for Windows 10 - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:09:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-uwp-controls-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-uwp-controls-in-wpf.html</guid>
        
        
        <category>uwp</category>
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅（概念篇）</title>
        <description>&lt;p&gt;OPML 全称是 &lt;strong&gt;Outline Processor Markup Language&lt;/strong&gt; ，即 &lt;strong&gt;大纲处理标记语言&lt;/strong&gt;。目前流行于收集博客的 RSS 源，便于用户转移自己的订阅项目。&lt;/p&gt;

&lt;p&gt;本文将介绍这个古老的格式，并提供一个 .NET 上的简易解析器。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文分为两个部分，一个是理解 OPML 格式，一个是解析此格式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-opml-for-rss-migrating&quot;&gt;概念篇（本文）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deserialize-opml-using-dotnet&quot;&gt;解析篇&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;opml-格式&quot;&gt;OPML 格式&lt;/h2&gt;

&lt;p&gt;RSS 订阅你应该并不陌生，你可以在我的博客上方看到 RSS 的订阅源按钮，也可以在各大博客站点发现这样的订阅按钮。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-23-feed-icon.svg&quot; alt=&quot;RSS 图标&quot; /&gt;&lt;br /&gt;
▲ RSS 图标&lt;/p&gt;

&lt;p&gt;图片来源于维基百科，如果你不太了解 RSS，可以直接前往 &lt;a href=&quot;https://zh.wikipedia.org/wiki/RSS&quot;&gt;RSS - 维基百科，自由的百科全书&lt;/a&gt; 查看或者自己搜索。&lt;/p&gt;

&lt;p&gt;OPML 是个古老的格式，第一个版本还是二十世纪六十年代的产物呢（详见 &lt;a href=&quot;http://dev.opml.org/spec1.html&quot;&gt;OPML 1.0 Specification&lt;/a&gt;）；只不过实际在用的 1.0 版本是 2000 年发布的，2.0 版本是 2007 年发布的。这么古老的格式也不妨碍它依然成为订阅源交换的标准格式。不过我们这篇文章不会去谈历史，我们只谈它的格式以及使用。&lt;/p&gt;

&lt;p&gt;OPML 官网对其作用的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The purpose of this format is to provide a way to exchange information between outliners and Internet services that can be browsed or controlled through an outliner.&lt;/p&gt;

  &lt;p&gt;OPML is also the file format for an outliner application, which is why OPML files may contain information about the size, position and expansion state of the window the outline is displayed in.&lt;/p&gt;

  &lt;p&gt;OPML has also become popular as a format for exchanging subscription lists between feed readers and aggregators.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其中最后一行的描述即交换订阅，尤其是 RSS 订阅。&lt;/p&gt;

&lt;h2 id=&quot;典型的-opml-文件&quot;&gt;典型的 OPML 文件&lt;/h2&gt;

&lt;p&gt;为了直观地了解 OPML 格式，我直接贴一个我的订阅的极简版文件内容。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;opml&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rss&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/feed.xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;htmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Team&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Team&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;林德熙&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;林德熙&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rss&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.lindexi.com/feed.xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;htmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blog.lindexi.com/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft .NET Blog&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft .NET Blog&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rss&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blogs.msdn.microsoft.com/dotnet/feed/&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;outline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft The Visual Studio Blog&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft The Visual Studio Blog&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rss&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blogs.msdn.microsoft.com/visualstudio/feed/&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/opml&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以很容易地看出它的一些特征。比如以 &lt;code class=&quot;highlighter-rouge&quot;&gt;opml&lt;/code&gt; 为根，&lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt; 中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 中包含分组的 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt;。每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;xmlUrl&lt;/code&gt; 等属性。接下来我们详细描述这个格式。&lt;/p&gt;

&lt;h2 id=&quot;opml-文件中的节点解释&quot;&gt;OPML 文件中的节点解释&lt;/h2&gt;

&lt;h3 id=&quot;opml-根节点&quot;&gt;opml 根节点&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;opml&amp;gt;&lt;/code&gt; 是 OPML 格式文件的根节点，其 &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 属性是必要的。它的值可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.0&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0&lt;/code&gt;；如果是 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.0&lt;/code&gt;，则视为符合 &lt;a href=&quot;http://dev.opml.org/spec1.html&quot;&gt;OPML 1.0 规范&lt;/a&gt;；如果是 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0&lt;/code&gt;，则视为符合 &lt;a href=&quot;http://dev.opml.org/spec2.html&quot;&gt;OPML 2.0 规范&lt;/a&gt;。额外的，值也可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.1&lt;/code&gt;，那么也视为符合 1.0 规范。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;opml&lt;/code&gt; 根节点中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 节点。&lt;/p&gt;

&lt;h3 id=&quot;head-节点&quot;&gt;head 节点&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt; 节点可包含 0 个或多个元素：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这就是 OPML 文档标题&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dateCreated&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文档创建时间&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dateModified&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文档修改时间&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ownerName&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文档作者&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ownerEmail&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文档作者的邮箱&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ownerId&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;文档作者的 url，要求不存在相同 Id 的两个作者&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;docs&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;描述此文档的文档的 url&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，这些都是可选的。&lt;/p&gt;

&lt;p&gt;额外的，还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;expansionState&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;vertScrollState&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;windowTop&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;windowLeft&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;windowBottom&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;windowRight&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;body-节点&quot;&gt;body 节点&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 节点包含一个或多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 元素。&lt;/p&gt;

&lt;h3 id=&quot;outline普通&quot;&gt;outline（普通）&lt;/h3&gt;

&lt;p&gt;outline 元素组成一个树状结构。也就是说，如果我们使用 OPML 储存 RSS 订阅列表，那么可以存为树状结构。在前面的例子中，我把自己的 RSS 订阅独立开来，把朋友和微软的 RSS 订阅分成了单独的组。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 必须有 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 属性，其他都是可选的。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 属性就是 RSS 订阅的显示文字，如果没有这个属性，那么 RSS 的订阅列表中将会是空白一片。&lt;/p&gt;

&lt;p&gt;于是，我们解析 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 属性便可以得到可以显示出来的 RSS 订阅列表。对于前面的例子对应的 RSS 订阅列表就可以显示成下面这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- walterlv
- Team
    - 林德熙
- Microsoft
    - Microsoft .NET Blog
    - Microsoft The Visual Studio Blog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 还有其他可选属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;指示此 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 节点应该如何解析&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isComment&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;布尔值，为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;；如果为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么次 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 就只是注释而已&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isBreakpoint&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;适用于脚本，执行时可下断点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;created&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;一个时间，表示此节点的创建时间&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;category&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;逗号分隔的类别：如果表示分类，则要用 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 分隔子类别；如果表示标签，则不加 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;例如：&lt;code class=&quot;highlighter-rouge&quot;&gt;/Boston/Weather&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/Harvard/Berkman,/Politics&lt;/code&gt;（例子来源于&lt;a href=&quot;http://dev.opml.org/spec2.html&quot;&gt;官方规范&lt;/a&gt;）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;outlinerss-专属&quot;&gt;outline（RSS 专属）&lt;/h3&gt;

&lt;p&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;rss&lt;/code&gt; 时，还有一些 RSS 专属属性。这时，必要属性就有三个了：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;xmlUrl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;xmlUrl&lt;/code&gt; 就指的是订阅源的 url 地址了。在官方规范中，规定解析器不应该总认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;text&lt;/code&gt; 存在，相比之下，&lt;code class=&quot;highlighter-rouge&quot;&gt;xmlUrl&lt;/code&gt; 显得更加重要。&lt;/p&gt;

&lt;p&gt;还有一些可选属性：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;description&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;htmlUrl&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;language&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;opml-的解析&quot;&gt;OPML 的解析&lt;/h2&gt;

&lt;p&gt;在了解了 OPML 的格式组成之后，便可以很容易的地解析此文件了。当然，我也写了一份 OPML 的解析，请参阅本文的第二部分，&lt;a href=&quot;/post/deserialize-opml-using-dotnet&quot;&gt;解析篇&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:08:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/using-opml-for-rss-migrating.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/using-opml-for-rss-migrating.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用 Postman 调试 ASP.NET Core 开发的 API</title>
        <description>&lt;p&gt;使用 ASP.NET Core 开发简单的后台 API 还是非常容易的。涉及到 GET 请求的调试我们可以用浏览器简单搞定，那么 POST/PUT/DELETE 这样的请求呢？&lt;/p&gt;

&lt;p&gt;本文将使用 Postman 来调试这些请求。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;简单的-aspnet-core-程序&quot;&gt;简单的 ASP.NET Core 程序&lt;/h2&gt;

&lt;p&gt;如果你还不清楚如何编写一个 ASP.NET Core 程序，可以阅读 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E4%BD%BF%E7%94%A8-asp-dotnet-core-%E5%81%9A-cs-%E7%A8%8B%E5%BA%8F.html&quot;&gt;win10 uwp 手把手教你使用 asp dotnet core 做 cs 程序&lt;/a&gt; 学习做一个最简单的版本。&lt;/p&gt;

&lt;p&gt;我们的重点不是写一个 ASP.NET Core 程序，所以我只贴出最简单的路由地址的处理。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WebApi.Rssman.Models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.WebApi.Rssman.Controllers&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;api/[controller]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RssController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RssFeedContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RssController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssFeedContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// GET: api/Rss&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssFeedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// GET: api/Rss/5&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssFeedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// POST: api/Rss&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RssFeedItem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// PUT: api/Rss/5&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpPut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RssFeedItem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// DELETE: api/ApiWithActions/5&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpDelete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略实现。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码是省略了所有实现的，完整的实现可以看这里：&lt;a href=&quot;https://github.com/walterlv/Rssman/blob/master/Rssman.Api/Controllers/RssController.cs&quot;&gt;RssController&lt;/a&gt;。相关数据模型类的定义可以看这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Rssman/blob/master/Rssman.Api/Models/RssFeedItem.cs&quot;&gt;RssFeedItem.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/Rssman/blob/master/Rssman.Api/Models/RssFeedContext.cs&quot;&gt;RssFeedContext.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上程序如果在 Visual Studio 里进行调试，可以在本地搭建一个可访问的 Url。比如: https://localhost:44395/ 。&lt;/p&gt;

&lt;h2 id=&quot;模拟-get-请求&quot;&gt;模拟 GET 请求&lt;/h2&gt;

&lt;p&gt;我们通过浏览器就可以模拟 GET 请求，比如我们在 Chrome / Microsoft Edge / Firefox 中访问 https://localhost:44395/api/rss 会在浏览器中显示结果的 json 字符串：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;feedUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://blog.walterlv.com/feed.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;siteUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://blog.walterlv.com/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;lindexi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;feedUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://blog.lindexi.com/feed.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;siteUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://blog.lindexi.com/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-19-54-25.png&quot; alt=&quot;Chrome 浏览器访问&quot; /&gt;&lt;br /&gt;
▲ Chrome 浏览器访问&lt;/p&gt;

&lt;p&gt;当然，实际上浏览器访问时是没有这些空白字符的，这样可以节省带宽。特别的，Internet Explorer 在访问时会提示保存 rss.json 文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-19-53-57.png&quot; alt=&quot;IE 浏览器访问&quot; /&gt;&lt;br /&gt;
▲ IE 浏览器访问&lt;/p&gt;

&lt;p&gt;很明显不用去管被时代淘汰的 IE 浏览器。&lt;/p&gt;

&lt;h2 id=&quot;下载安装-postman&quot;&gt;下载安装 Postman&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.getpostman.com/&quot;&gt;Postman&lt;/a&gt; 的下载地址在这里 &lt;a href=&quot;https://www.getpostman.com/apps&quot;&gt;https://www.getpostman.com/apps&lt;/a&gt;，&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-19-58-04.png&quot; alt=&quot;选择你需要的平台&quot; /&gt;&lt;br /&gt;
▲ 选择你需要的平台&lt;/p&gt;

&lt;p&gt;Postman 的安装是极简的，没有任何设置。当启动后，注册或登录你的个人账号，然后填写一些个性化设置即可。&lt;/p&gt;

&lt;p&gt;如果你是本地 https 的调试，记得在 Postman 里关掉 SSL 证书验证，不然这种自己签署的证书是无法成功完成请求的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-04-23.png&quot; alt=&quot;关闭 SSL 证书验证&quot; /&gt;&lt;br /&gt;
▲ 关闭 SSL 证书验证&lt;/p&gt;

&lt;h2 id=&quot;模拟-post-请求&quot;&gt;模拟 POST 请求&lt;/h2&gt;

&lt;p&gt;在 Postman 的主界面，创建一个 HTTP POST 请求只需要几个小步骤：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-08-33.png&quot; alt=&quot;创建一个 POST 请求&quot; /&gt;&lt;br /&gt;
▲ 创建一个 POST 请求&lt;/p&gt;

&lt;p&gt;“Send” 按钮点击后，我们便可以在右侧看到此请求的响应：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-10-52.png&quot; alt=&quot;请求响应&quot; /&gt;&lt;br /&gt;
▲ 请求响应&lt;/p&gt;

&lt;p&gt;注意，如果你看到的是下面这样的响应界面，记得回到前面的步骤去关闭 SSL 证书验证。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-11-59.png&quot; alt=&quot;无法获取响应&quot; /&gt;&lt;br /&gt;
▲ 无法获取响应&lt;/p&gt;

&lt;p&gt;如果你在 Visual Studio 中打了断点，那么现在应该已经进入了断点了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-14-24.png&quot; alt=&quot;Visual Studio 中进入断点&quot; /&gt;&lt;br /&gt;
▲ Visual Studio 中进入断点&lt;/p&gt;

&lt;p&gt;于是你就能调试 POST 请求了。&lt;/p&gt;

&lt;h2 id=&quot;模拟-put--patch--delete---请求&quot;&gt;模拟 PUT / PATCH / DELETE / … 请求&lt;/h2&gt;

&lt;p&gt;同样的，你也可以用 Postman 模拟其他种类的 HTTP 请求。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-09-20-16-11.png&quot; alt=&quot;模拟其他请求&quot; /&gt;&lt;br /&gt;
▲ 模拟其他请求&lt;/p&gt;

&lt;h2 id=&quot;关于本文调试的-aspnet-程序-rssman&quot;&gt;关于本文调试的 ASP.NET 程序 Rssman&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/walterlv/rssman&quot;&gt;Rssman&lt;/a&gt; 是用来管理 RSS 订阅的 ASP.NET 程序，目前正在开发中。&lt;/p&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:07:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-postman-to-debug-asp-net-core-api.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-postman-to-debug-asp-net-core-api.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>asp</category>
        
      </item>
    
      <item>
        <title>项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦）</title>
        <description>&lt;p&gt;知道了 csproj 文件中的一些常用 NuGet 属性，创建 NuGet 包时就可以充分发挥新 Sdk 自动生成 NuGet 包的优势，不需要 nuspec 文件啦。（毕竟 nuspec 文件没有 .csproj 和 .targets 文件强大而又有扩展性。）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;“项目文件中的已知属性系列”分为两个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/known-properties-in-csproj&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;本文：&lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NuGet 相关的属性也分为全局属性和项属性两类。不过，我更愿意分成三类来说明：&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;nuspec-属性&quot;&gt;nuspec 属性&lt;/h2&gt;

&lt;p&gt;当然，这部分的属性也是在 csproj 中使用的，是为了生成 nuspec 文件。&lt;/p&gt;

&lt;p&gt;使用方法像这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageId&amp;gt;&lt;/span&gt;Walterlv.Demo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageVersion&amp;gt;&lt;/span&gt;3.2.0-beta&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net46&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过我们通常没有这么直接去设置，因为大多数属性都是有默认值的，如果不设置，将自动使用默认值。甚至什么都不写也能生成正确的 nuspec 文件。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageId)&lt;/code&gt;: NuGet 包的唯一 Id，对应 NuGet 的 Id 属性。这个 Id 需要在整个服务器（例如 nuget.org）上唯一，如果没设置，则使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(AssemblyName)&lt;/code&gt;；例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Newtonsoft.Json&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageVersion)&lt;/code&gt;: NuGet 包的包版本，可以使用语义版本号（参见&lt;a href=&quot;/post/semantic-version&quot;&gt;语义版本号（Semantic Versioning） - 吕毅&lt;/a&gt;），如果没设置，则使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(Version)&lt;/code&gt;；例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;3.2.0-beta&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageVersionPrefix)&lt;/code&gt;: 包版本前缀，默认为空。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageVersionSuffix)&lt;/code&gt;: 包版本后缀，默认为空。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Authors)&lt;/code&gt;: 包的作者；建议指定成在 nuget.org 上的用户名，这样访客可以点击包作者查看到包作者的信息；多个名字用分号分隔。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Title)&lt;/code&gt;: 包的显示名称，如果没设置，则使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageId)&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageDescription)&lt;/code&gt;: 包的描述文字，如果填写了，则用户在浏览包的时候可以看到。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Copyright)&lt;/code&gt;: 包的版权声明&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageRequireLicenseAcceptance)&lt;/code&gt;: 是个布尔值，如果为 true，则在安装包之前要求同意协议。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageLicenseUrl)&lt;/code&gt;: 此 NuGet 包协议所在的 url。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageProjectUrl)&lt;/code&gt;: 此 NuGet 包的项目 url。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageIconUrl)&lt;/code&gt;: 此 NuGet 包的图标 url，无论是 nuget.org 还是 Visual Studio 都将从这个 url 下载包的图标。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageTags)&lt;/code&gt;: 标签，用分号分隔；指定多个标签有助于用户在 nuget.org 上搜索到你的 NuGet 包。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageReleaseNotes)&lt;/code&gt;: 这个版本的 Release 记录。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryUrl)&lt;/code&gt;: 仓库 url，例如 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer.git&quot;&gt;https://github.com/dotnet-campus/MSTestEnhancer.git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryType)&lt;/code&gt;: 仓库类型，例如 git、tfs。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryBranch)&lt;/code&gt;: &lt;strong&gt;NuGet 4.7 才开始的新属性！&lt;/strong&gt;此包对应的仓库分支，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryCommit)&lt;/code&gt;: &lt;strong&gt;NuGet 4.7 才开始的新属性！&lt;/strong&gt;此包对应的提交号，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;2d3ef96ee704d7896eeb2d88fbc987b2004ff786&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageType)&lt;/code&gt;: &lt;em&gt;我还没有理解到此属性的作用。&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上有些信息在每次 NuGet 发布之前都是要改的，例如：&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageVersion)&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageReleaseNotes)&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryCommit)&lt;/code&gt;。所以很明显——&lt;strong&gt;这不是用来给开发者设置的属性，是用于辅助我们生成打包工具的。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;配置属性&quot;&gt;配置属性&lt;/h2&gt;

&lt;p&gt;这些属性会影响生成 NuGet 包的过程。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此程序集不可打包，通常在单元测试项目中设置此属性。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsPackable&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsPackable&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Description&amp;gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这是一个仅开发阶段使用的 NuGet 包，详情请参见 https://blog.walterlv.com/post/prevent-nuget-package-been-depended.html。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 单独指定 NuGet 包应该输出到哪个目录（可以跟项目文件的输出目录不一样）。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageOutputPath&amp;gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果指定为 true，那么还会额外生成 PackageId.symbols.nupkg 包，
         除了原有包的内容外，还额外包含全部的输出文件，以及源码和项目文件，用于调试。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeSymbols&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeSymbols&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 大致与 IncludeSymbols 相同，不过只会额外把 pdb 和 Compile 类型的文件打包到 NuGet 包中。
         如果使用 ProjectReference 引用的项目没有指定 TreatAsPackageReference=false，也会一起被打包。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeSource&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeSource&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageTypes&amp;gt;&amp;lt;/PackageTypes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果指定为 true，那么生成的 dll 将拷贝到 NuGet 包的 tools 目录下。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsTool&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsTool&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果 lib/**/*dll 中没有发现 dll，NuGet 打包过程中会有警告；
         将这个属性设为 true 可以禁用警告；这在制作纯工具型 NuGet 包是非常有用。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoPackageAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoPackageAnalysis&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;MinClientVersion&amp;gt;&amp;lt;/MinClientVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeContentInPack&amp;gt;&amp;lt;/IncludeContentInPack&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 默认情况下，项目输出的 dll 会被打包到 lib 目录下；
         设置了此属性后，就可以打包到其他目录下了。此例打包到 task 目录下 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildOutputTargetFolder&amp;gt;&lt;/span&gt;tasks&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildOutputTargetFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentTargetFolders&amp;gt;&amp;lt;/ContentTargetFolders&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 以下属性都是为了使用单独的 nuspec 文件而准备的；如果不使用 nuspec 文件，通常无需设置这些属性。 --&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 默认情况下，使用 dotnet pack 打 NuGet 包时，也会顺便编译；
         但设置此值为 true 后，就会像 nuget.exe 那样不进行编译了。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 默认是 true，如果指定为 false，那么项目编译输出的 dll 文件将不会被打包到 NuGet 包中。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeBuildOutput&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeBuildOutput&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果需要额外手工编写 nuspec 文件，那么使用此属性指定绝对或相对路径。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuspecFile&amp;gt;&lt;/span&gt;Walterlv.Demo.nuspec&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuspecFile&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 生成的属性可以时 nuspec 文件中的占位符生效，
         例如 &amp;lt;file src=&quot;$SampleProperty$&quot; target=&quot;src/&quot; /&amp;gt;  --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuspecProperties&amp;gt;&lt;/span&gt;SampleProperty=Program.cs&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NuspecProperties&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果 NuspecFile 使用相对路径，那么就会相对于此路径；通常不需要指定。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NuspecBasePath&amp;gt;&amp;lt;/NuspecBasePath&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;DevelopmentDependency&lt;/code&gt; 的设置可参见：&lt;a href=&quot;/post/prevent-nuget-package-been-depended&quot;&gt;帮助官方 NuGet 解掉 Bug，制作绝对不会传递依赖的 NuGet 包 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;以上没有设置值和注释的属性，我正在查阅资料。&lt;/p&gt;

&lt;h2 id=&quot;项属性&quot;&gt;项属性&lt;/h2&gt;

&lt;h3 id=&quot;文件&quot;&gt;文件&lt;/h3&gt;

&lt;p&gt;为了脱离 nuspec 文件来打包，csproj 中需要对特殊用途的文件设置特别的 NuGet 属性。&lt;/p&gt;

&lt;p&gt;例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Pack&lt;/code&gt; 属性可以额外指定一或一组通配符文件需要被打包到 NuGet 包中；&lt;code class=&quot;highlighter-rouge&quot;&gt;PackagePath&lt;/code&gt; 则指定了打包到 NuGet 包的路径（&lt;em&gt;NuGet 会通过扩展名来自动识别这是文件夹还是文件，所以可以通过这个属性来重新指定名称，但无法重新指定扩展名&lt;/em&gt;）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;readme.txt&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Pack&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Pack&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackagePath&amp;gt;&lt;/span&gt;\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackagePath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Content&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PackageId.targets&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Pack&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Pack&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackagePath&amp;gt;&lt;/span&gt;buildMultiTargeting\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackagePath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Content&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;引用&quot;&gt;引用&lt;/h3&gt;

&lt;p&gt;引用中也可以加入一些 NuGet 包的生成属性。&lt;/p&gt;

&lt;p&gt;无论是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ProjectReference /&amp;gt;&lt;/code&gt; 还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PackageReference /&amp;gt;&lt;/code&gt;，都可以额外加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ExcludeAssets&amp;gt;&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/code&gt; 属性。&lt;/p&gt;

&lt;p&gt;使用方法类似这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.0.0-beta&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;all&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeAssets&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ExcludeAssets&amp;gt;&lt;/span&gt;contentFiles&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ExcludeAssets&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/span&gt;contentFiles;analyzers&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReference&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.0.0-beta&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不区分大小写。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/code&gt; 引用的项目或包中的指定部分是本项目的依赖项。默认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;all&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ExcludeAssets&amp;gt;&lt;/code&gt; 引用的项目或包中的指定部分不是本项目的依赖项，应该排除。默认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;none&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/code&gt; 引用的项目或包中的指定部分依然是本项目的依赖项，但是在打 NuGet 包时不作为依赖项（不会传递到下一个项目）。默认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;contentfiles;analyzers;build&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你正试图用 NuGet 编写一个编译时工具，那么，你可能需要在所有引用的最后加上如下行，将所有的包引用都设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrivateAssets&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(PackageReference)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你希望了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Reference&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageReference&lt;/code&gt; 以及上面 &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; 的含义，可以阅读我的另一篇文章：&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;可能没有开放的内部属性&quot;&gt;可能没有开放的内部属性&lt;/h2&gt;

&lt;p&gt;在 Microsoft.NET.Sdk 中，NuGet 包的打包主要靠的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGet.Build.Tasks.Pack.targets&lt;/code&gt; 文件中一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackTask&lt;/code&gt; 的任务来完成的，它是一个使用了非常多参数的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackItem=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackProjectInputFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_PackageFiles)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageFilesToExclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_PackageFilesToExclude)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageVersion)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageId=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageId)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(Title)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Authors=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(Authors)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Description=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageDescription)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Copyright=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(Copyright)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;RequireLicenseAcceptance=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageRequireLicenseAcceptance)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;LicenseUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageLicenseUrl)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ProjectUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageProjectUrl)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IconUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageIconUrl)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ReleaseNotes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageReleaseNotes)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Tags=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageTags)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DevelopmentDependency=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(DevelopmentDependency)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;BuildOutputInPackage=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_BuildOutputInPackage)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ProjectReferencesWithVersions=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ProjectReferencesWithVersions)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;TargetPathsToSymbols=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_TargetPathsToSymbols)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;TargetFrameworks=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_TargetFrameworks)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;AssemblyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AssemblyName)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IncludeSymbols=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeSymbols)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IncludeSource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeSource)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageTypes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageType)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IsTool=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IsTool)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;RepositoryUrl=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(RepositoryUrl)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;RepositoryType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(RepositoryType)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_SourceFiles-&amp;gt;Distinct())&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NoPackageAnalysis=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NoPackageAnalysis)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;MinClientVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MinClientVersion)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Serviceable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(Serviceable)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FrameworkAssemblyReferences=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_FrameworkAssemblyReferences)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ContinuePackingAfterGeneratingNuspec=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ContinuePackingAfterGeneratingNuspec)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NuspecOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuspecOutputAbsolutePath)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IncludeBuildOutput=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeBuildOutput)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;BuildOutputFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BuildOutputTargetFolder)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ContentTargetFolders=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ContentTargetFolders)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;RestoreOutputPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(RestoreOutputAbsolutePath)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NuspecFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuspecFileAbsolutePath)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NuspecBasePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuspecBasePath)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NuspecProperties=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(NuspecProperties)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;AllowedOutputExtensionsInPackageBuildOutputFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AllowedOutputExtensionsInPackageBuildOutputFolder)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以总结起来我们还有这些 NuGet 的属性还可以配置（想必下划线开头的属性或集合是 NuGet 内部不愿意公开的属性了）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackProjectInputFile)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFiles)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFilesToExclude)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageVersion)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageId)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Title)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Authors)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageDescription)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Copyright)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageRequireLicenseAcceptance)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageLicenseUrl)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageProjectUrl)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageIconUrl)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageReleaseNotes)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageTags)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(DevelopmentDependency)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_BuildOutputInPackage)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_ProjectReferencesWithVersions)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_TargetPathsToSymbols)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_TargetFrameworks)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(AssemblyName)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageOutputAbsolutePath)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IncludeSymbols)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IncludeSource)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(PackageType)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IsTool)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryUrl)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RepositoryType)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_SourceFiles-&amp;gt;Distinct())&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(NoPackageAnalysis)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(MinClientVersion)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(Serviceable)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;@(_FrameworkAssemblyReferences)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(ContinuePackingAfterGeneratingNuspec)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(NuspecOutputAbsolutePath)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(IncludeBuildOutput)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(BuildOutputTargetFolder)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(ContentTargetFolders)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(RestoreOutputAbsolutePath)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(NuspecFileAbsolutePath)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(NuspecBasePath)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(NuspecProperties)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(AllowedOutputExtensionsInPackageBuildOutputFolder)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$(AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是 .NET Core 2.1 中自带的 NuGet 的打包属性，比 .NET Core 2.0 中多了个 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_SourceFiles-&amp;gt;Distinct())&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets?wt.mc_id=MVP&quot;&gt;NuGet pack and restore as MSBuild targets - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files?wt.mc_id=MVP&quot;&gt;NuGet PackageReference format (package references in project files) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 09 Mar 2019 01:07:32 +0000</pubDate>
        <link>https://blog.walterlv.com/post/known-nuget-properties-in-csproj.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/known-nuget-properties-in-csproj.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>为什么 C# 的 string.Empty 是一个静态只读字段，而不是一个常量呢？</title>
        <description>&lt;p&gt;使用 C# 语言编写字符串常量的时候，你可能会发现可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; 而不能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt;。进一步可以发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 实际上是一个静态只读字段，而不是一个常量。&lt;/p&gt;

&lt;p&gt;为什么这个看起来最适合是常量的 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt;，竟然使用静态只读字段呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;stringempty&quot;&gt;string.Empty&lt;/h2&gt;

&lt;p&gt;这个问题，我们需要去看 .NET Core 的源码（当然 .NET Framework 也是一样的）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Intrinsic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;值得注意的是上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Intrinsic&lt;/code&gt; 特性。&lt;/p&gt;

&lt;h2 id=&quot;intrinsic-特性&quot;&gt;Intrinsic 特性&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Intrinsic&lt;/code&gt; 特性的注释是这样的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Calls to methods or references to fields marked with this attribute may be replaced at some call sites with jit intrinsic expansions.&lt;br /&gt;
Types marked with this attribute may be specially treated by the runtime/compiler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;翻译过来是：对具有此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Intrinsic&lt;/code&gt; 特性标记的字段的方法或引用的调用可以在某些具有 JIT 内部扩展的调用点处替换，标记有此属性的类型可能被运行时或编译器特殊处理。&lt;/p&gt;

&lt;p&gt;也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 字段并不是一个普通的字段，对它的调用会被特殊处理。但是是如何特殊处理呢？&lt;/p&gt;

&lt;h2 id=&quot;jit-编译器&quot;&gt;JIT 编译器&lt;/h2&gt;

&lt;p&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 的注释是这样描述的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Empty constant holds the empty string value. It is initialized by the EE during startup. It is treated as intrinsic by the JIT as so the static constructor would never run. Leaving it uninitialized would confuse debuggers.&lt;br /&gt;
We need to call the String constructor so that the compiler doesn’t mark this as a literal. Marking this as a literal would mean that it doesn’t show up as a field which we can access from native.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;翻译过来是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Empty 常量保存的是空字符串的值，它在启动期间由执行引擎初始化。它被 JIT 视为内在的，因此静态构造函数永远不会运行。将它保持为未初始化的状态将会使得调试器难以解释此行为。&lt;br /&gt;
于是我们需要调用 String 的构造函数，以便编译器不会将其标记为文字。将其标记为文字将意味着它不会显示为我们可以从本机代码访问的字段。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;说明一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;注释里的 EE 是 Execution Engine 的缩写，其实也就是 CLR 运行时。&lt;/li&gt;
  &lt;li&gt;那个 literal 我翻译成了文字。实际上这里说的是 IL 调用字符串时的一些区别：
    &lt;ul&gt;
      &lt;li&gt;在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; 时使用的 IL 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldstr &quot;&quot;&lt;/code&gt;（Load String Literal）&lt;/li&gt;
      &lt;li&gt;而在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 时使用的 IL 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldsfld string [mscorlib]System.String::Empty&lt;/code&gt;（Load Static Field）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;虽然 IL 在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 时生成的 IL 不同，但是在 JIT 编译成本机代码的时候，生成的代码完全一样。
    &lt;ul&gt;
      &lt;li&gt;详情请参见：&lt;a href=&quot;https://stackoverflow.com/a/3674336/6233938&quot;&gt;.net - What’s the different between ldsfld and ldstr in IL? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;我写过一篇文章 &lt;a href=&quot;/post/same-strings-at-compile-time-are-the-same-instances-at-runtime&quot;&gt;.NET/C# 编译期间能确定的相同字符串，在运行期间是相同的实例 - 吕毅&lt;/a&gt;。虽然一般情况下取字符串常量实例的时候会去字符串池，但是不用担心取 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; 会造成性能问题，因为实际上 JIT 编译器已经特殊处理了，不会去找池子。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 字段在整个 &lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类型中你都看不到初始化的代码，&lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类的静态构造函数也不会执行。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类中的所有静态成员都不会被托管代码初始化。&lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 的静态初始化过程都是由 CLR 运行时进行的，而这部分的初始化是本机代码实现的。&lt;/p&gt;

&lt;p&gt;那本机代码又是如何初始化 &lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类型的呢？在 CLR 运行时的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain::SetupSharedStatics()&lt;/code&gt; 方法中实现，可前往 GitHub 阅读这部分的源码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/coreclr/blob/ef1e2ab328087c61a6878c1e84f4fc5d710aebce/src/vm/appdomain.cpp#L7735&quot;&gt;coreclr/appdomain.cpp at ef1e2ab328087c61a6878c1e84f4fc5d710aebce · dotnet/coreclr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This is a convenient place to initialize String.Empty.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// It is treated as intrinsic by the JIT as so the static constructor would never run.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Leaving it uninitialized would confuse debuggers.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// String should not have any static constructors.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_ASSERTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_pStringClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsClassPreInited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;FieldDesc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pEmptyStringFD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MscorlibBinder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FIELD__STRING__EMPTY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OBJECTREF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pEmptyStringHandle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECTREF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TADDR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pLocalModule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPrecomputedGCStaticsBasePointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pEmptyStringFD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SetObjectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pEmptyStringHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetEmptyString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结为什么-stringempty-需要是一个静态只读字段而不是常量&quot;&gt;总结：为什么 string.Empty 需要是一个静态只读字段而不是常量？&lt;/h2&gt;

&lt;p&gt;从上文中 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 的注释描述中可以知道：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;编译器会将 C# 语言编译成中间语言 MSIL；&lt;/li&gt;
  &lt;li&gt;如果这是一个常量，那么编译器在不做特殊处理的情况下，就会生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldstr &quot;&quot;&lt;/code&gt;，而这种方式不会调用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类的构造函数（注意不是静态构造函数，&lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类的静态构造函数是特殊处理不会调用的）；&lt;/li&gt;
  &lt;li&gt;而如果这是一个静态字段，那么编译器可以在不做特殊处理的情况下，生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ldsfld string [mscorlib]System.String::Empty&lt;/code&gt;，这在首次执行时会触发 &lt;code class=&quot;highlighter-rouge&quot;&gt;String&lt;/code&gt; 类的构造函数，并在本机代码（非托管代码）中完成初始化。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，事实上编译器也可以针对此场景做特殊处理，但为什么不是在编译这一层进行特殊处理，我已经找不到出处了。&lt;/p&gt;

&lt;h2 id=&quot;本文引申的其他问题&quot;&gt;本文引申的其他问题&lt;/h2&gt;

&lt;h3 id=&quot;能否反射修改-stringempty-的值&quot;&gt;能否反射修改 string.Empty 的值？&lt;/h3&gt;

&lt;p&gt;不行！&lt;/p&gt;

&lt;p&gt;实际上，在 .NET Framework 4.0 及以前是可以反射修改其值的，这会造成相当多的基础组件不能正常工作，在 .NET Framework 4.5 和以后的版本，以及 .NET Core 中，CLR 运行时已经不允许你做出这么出格儿的事了。&lt;/p&gt;

&lt;p&gt;不过，如果你使用不安全代码（&lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt;）来修改这个字段的值就当我没说。关于使用不安全代码转换字符串的方法可以参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/C-%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%A6%96%E5%AD%97%E7%AC%A6%E5%A4%A7%E5%86%99.html&quot;&gt;C＃ 字符串首字符大写 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/same-strings-at-compile-time-are-the-same-instances-at-runtime&quot;&gt;.NET/C# 编译期间能确定的相同字符串，在运行期间是相同的实例 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-和-stringempty-到底有什么区别&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt; 到底有什么区别？&lt;/h3&gt;

&lt;p&gt;从前文你可以得知，在运行时级别，这两者 &lt;strong&gt;没有任何区别&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;于是，当你需要一个代表 “空字符串” 含义的时候，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt;；而当你必须要一个常量时，就使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/src/System/String.CoreCLR.cs,c9f70a27facb27cf&quot;&gt;String.CoreCLR.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs,0b1553fdd9183e62,references&quot;&gt;Intrinsic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/24811218&quot;&gt;在C#中 String.Empty和 “” 有什么区别？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/3674336/6233938&quot;&gt;.net - What’s the different between ldsfld and ldstr in IL? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Mar 2019 15:54:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/why-string-empty-is-a-readonly-field-but-not-a-constant.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/why-string-empty-is-a-readonly-field-but-not-a-constant.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 调试的时候显示自定义的调试信息（DebuggerDisplay 和 DebuggerTypeProxy）</title>
        <description>&lt;p&gt;使用 Visual Studio 调试 .NET 程序的时候，在局部变量窗格或者用鼠标划到变量上就能查看变量的各个字段和属性的值。默认显示的是对象 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 方法调用之后返回的字符串，不过如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 已经被占作它用，或者我们只是希望在调试的时候得到我们最希望关心的信息，则需要使用 .NET 中调试器相关的特性。&lt;/p&gt;

&lt;p&gt;本文介绍使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplayAttribute&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerTypeProxyAttribute&lt;/code&gt; 来自定义调试信息的显示。（同时隐藏我们在背后做的这些见不得人的事儿。）&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;示例代码&quot;&gt;示例代码&lt;/h2&gt;

&lt;p&gt;比如我们有一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLine&lt;/code&gt; 的类型，表示从命令行传入的参数；内有一个字典，包含命令行参数的所有信息。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLine&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们在 Visual Studio 里面调试得到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLine&lt;/code&gt; 的实例，然后使用调试器查看这个实例的属性、字段和集合。&lt;/p&gt;

&lt;p&gt;然后，这样的一个字典嵌套列表的类型，竟然需要点开 4 层才能知道命令行参数究竟是什么。这样的调试效率显然是太低了！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-22-30-28.png&quot; alt=&quot;原生的调试显示&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;debuggerdisplay&quot;&gt;DebuggerDisplay&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplayAttribute&lt;/code&gt; 可以帮助我们直接在局部变量窗格或者鼠标划过的时候就看到对象中我们最希望了解的信息。&lt;/p&gt;

&lt;p&gt;现在，我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLine&lt;/code&gt; 上加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplayAttribute&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 此段代码非最终版本。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerDisplay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommandLine: {DebuggerDisplay}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLine&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggerDisplay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;效果有了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-22-36-42.png&quot; alt=&quot;使用 DebuggerDisplay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过，展开对象查看的时候可以看到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplay&lt;/code&gt; 的属性，而这个属性我们只是调试使用，这是个垃圾属性，并不应该影响我们的查看。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-22-39-20.png&quot; alt=&quot;里面有一个 DebuggerDisplay 垃圾属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerBrowsable&lt;/code&gt; 特性可以关闭某个属性或者字段在调试器中的显示。于是代码可以改进为：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  [DebuggerDisplay(&quot;CommandLine: {DebuggerDisplay}&quot;)]
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  [DebuggerDisplay(&quot;CommandLine: {DebuggerDisplay,nq}&quot;)]
&lt;/span&gt;    public class CommandLine
    {
        private readonly Dictionary&amp;lt;string, IReadOnlyList&amp;lt;string&amp;gt;&amp;gt; _optionArgs;
        private CommandLine(Dictionary&amp;lt;string, IReadOnlyList&amp;lt;string&amp;gt;&amp;gt; optionArgs)
            =&amp;gt; _optionArgs = optionArgs ?? throw new ArgumentNullException(nameof(optionArgs));
    
&lt;span class=&quot;gi&quot;&gt;++      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
&lt;/span&gt;        private string DebuggerDisplay =&amp;gt; string.Join(' ', _optionArgs
            .Select(pair =&amp;gt; $&quot;{pair.Key}{(pair.Key == null ? &quot;&quot; : &quot; &quot;)}{string.Join(' ', pair.Value)}&quot;));
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;添加了从不显示此字段（&lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerBrowsableState.Never&lt;/code&gt;），在调试的时候，展开后的属性列表里面没有垃圾 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplay&lt;/code&gt; 属性了。&lt;/p&gt;

&lt;p&gt;另外，我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplay&lt;/code&gt; 特性的中括号中加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;nq&lt;/code&gt; 标记（No Quote）来去掉最终显示的引号。&lt;/p&gt;

&lt;h2 id=&quot;debuggertypeproxy&quot;&gt;DebuggerTypeProxy&lt;/h2&gt;

&lt;p&gt;虽然我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerDisplay&lt;/code&gt; 使得命令行参数一眼能看出来，但是看不出来我们把命令行解析成什么样了。于是我们需要更精细的视图。&lt;/p&gt;

&lt;p&gt;然而，上面展开 &lt;code class=&quot;highlighter-rouge&quot;&gt;_optionArgs&lt;/code&gt; 字段的时候，依然需要展开 4 层才能看到我们的所有信息，所以我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerTypeProxyAttribute&lt;/code&gt; 来优化调试器实例内部的视图。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLineDebugView&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandLine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLineDebugView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CommandLine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootHidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我面写了一个新的类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLineDebugView&lt;/code&gt;，并在构造函数中允许传入要优化显示的类型的实例。在这里，我们写一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 属性把原来字典里面需要四层才能展开的值合并成一个字符串集合。&lt;/p&gt;

&lt;p&gt;但是，我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 上标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerBrowsableState.RootHidden&lt;/code&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果这是一个集合，那么这个集合将直接显示到调试视图的上一级视图中；&lt;/li&gt;
  &lt;li&gt;如果这是一个普通对象，那么这个对象的各个属性字段将合并到上一级视图中显示。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;别忘了我们还需要禁止 &lt;code class=&quot;highlighter-rouge&quot;&gt;_owner&lt;/code&gt; 在调试器中显示，然后把 &lt;code class=&quot;highlighter-rouge&quot;&gt;[DebuggerTypeProxy(typeof(CommandLineDebugView))]&lt;/code&gt; 加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLine&lt;/code&gt; 类型上。&lt;/p&gt;

&lt;p&gt;这样，最终的显示效果是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-22-51-26.png&quot; alt=&quot;使用 DebuggerTypeProxy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;Raw View&lt;/code&gt; 可以看到我们没有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggerTypeProxyAttribute&lt;/code&gt; 视图时的属性和字段。&lt;/p&gt;

&lt;h2 id=&quot;最终代码&quot;&gt;最终代码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Framework.StateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Framework&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerDisplay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommandLine: {DebuggerDisplay,nq}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerTypeProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CommandLineDebugView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLine&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optionArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggerDisplay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLineDebugView&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandLine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLineDebugView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CommandLine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootHidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_optionArgs&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggertypeproxyattribute&quot;&gt;DebuggerTypeProxyAttribute Class (System.Diagnostics) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggerdisplayattribute&quot;&gt;DebuggerDisplayAttribute Class (System.Diagnostics) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/debugger/using-debuggertypeproxy-attribute&quot;&gt;Using DebuggerTypeProxy Attribute - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute&quot;&gt;Using the DebuggerDisplay Attribute - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Mar 2019 14:53:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/display-instance-info-in-custom-debugger-view.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/display-instance-info-in-custom-debugger-view.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>透明度叠加算法：如何计算半透明像素叠加到另一个像素上的实际可见像素值（附 WPF 和 HLSL 的实现）</title>
        <description>&lt;p&gt;本文介绍透明度叠加算法（Alpha Blending Algorithm），并用 C#/WPF 的代码，以及像素着色器的代码 HLSL 来实现它。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;算法&quot;&gt;算法&lt;/h2&gt;

&lt;p&gt;对于算法，我只是搬运工，可以随意搜索到。算法详情请查看：&lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_compositing&quot;&gt;Alpha compositing - Wikipedia&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;对于完全不透明的背景和带有透明度的前景，合并算法为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是红色。然后绿色 &lt;code class=&quot;highlighter-rouge&quot;&gt;g&lt;/code&gt; 和蓝色 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 通道进行一样的计算。最终合成图像的透明通道始终设置为 1。&lt;/p&gt;

&lt;h2 id=&quot;在-c-代码中实现&quot;&gt;在 C# 代码中实现&lt;/h2&gt;

&lt;p&gt;多数 UI 框架对于颜色值的处理都是用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;byte&lt;/code&gt; 赛表单个通道的一个像素。于是计算会采用 0xff 即 255。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;blue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码当然是跑不起来的，因为是下面两篇博客的魔改代码。你需要阅读以下两篇博客了解如何在 WPF 中按像素修改图像，然后应用上面的透明度叠加代码。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/WPF-%E4%BF%AE%E6%94%B9%E5%9B%BE%E7%89%87%E9%A2%9C%E8%89%B2.html?nsukey=3TnZtVDUa%2BAnFMJeDMHwZ4cjmTsA4717d6Ze0gKK9BGnAOIN6KFqtb9%2BS67a2fBbYovvCCLci%2FLCroDOBgYN1jPFIlS1r2yxW8qNZV3SWEQntwVj5PXycG0qkrfmXgcibPr8OUsqrNSzzHTjWRam0%2FgjmHiOCIpqccEk3UEcjlNmuv8N9Jn6klOC8GZ%2FeizvB0JAy9o824%2BxM%2Bzf%2BH3Egw%3D%3D&quot;&gt;WPF 修改图片颜色&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.gitee.io/post/WPF-%E9%80%9A%E8%BF%87%E4%BD%8D%E5%A4%84%E7%90%86%E5%90%88%E5%B9%B6%E5%9B%BE%E7%89%87.html?nsukey=ak1Q2mctZhk%2BL1VqK8fq6O05g7K4kQpAlgOWzv8UkoBwH6YHbJMncmmUMCEFCoJH1nuxZuIoTRZ0UB89uHOAzWZxs3MbPH1Lnjyp527FWdN%2FOJaP93QxT0VxIKz5TZYrvLboSjnvEH27Bj9i2WXP556mZBC4WOAlc93mfYOR3aJKBe%2F78uEVBbVMsyWrdGIS8sFxbXebypVQFibs24lzXw%3D%3D&quot;&gt;WPF 通过位处理合并图片&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;话说，一般 UI 框架都自带有透明度叠加，为什么还要自己写一份呢？&lt;/p&gt;

&lt;p&gt;当然是因为某些场景下我们无法使用到 UI 框架的透明度叠加特性的时候。例如使用 HLSL 编写像素着色器的一个实现。&lt;/p&gt;

&lt;p&gt;下面使用像素着色器的实现是我曾经写过的一个特效的一个小部分，我把透明度叠加的部分单独摘取出来。&lt;/p&gt;

&lt;h2 id=&quot;在像素着色器中实现&quot;&gt;在像素着色器中实现&lt;/h2&gt;

&lt;p&gt;以下是 HLSL 代码的实现。Background 是从采样寄存器 0 取到的颜色采样，Foreground 是从采样寄存器 1 取到的颜色采样。&lt;/p&gt;

&lt;p&gt;这里的计算中，背景是不带透明度的，而前景是带有透明度的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;description&amp;gt;透明度叠加效果。&amp;lt;/description&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foreground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TEXCOORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COlOR&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tex2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tex2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-14-30-00.png&quot; alt=&quot;叠加了一个带有透明度的图片&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果要测试的图片都是不带透明度的，那么可以通过自己设一个透明度来模拟，传入透明度值 Alpha。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;description&amp;gt;透明度叠加效果。&amp;lt;/description&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;type&amp;gt;Double&amp;lt;/type&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;采样 2 的叠加透明度。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;minValue&amp;gt;0.0&amp;lt;/minValue&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;maxValue&amp;gt;1.0&amp;lt;/maxValue&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;defaultValue&amp;gt;0.75&amp;lt;/defaultValue&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Alpha&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foreground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TEXCOORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COlOR&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tex2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tex2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foreground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-05-alpha-blending.gif&quot; alt=&quot;为第二张采样设定透明度&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_compositing&quot;&gt;Alpha compositing - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/9014763/6233938&quot;&gt;algorithm - Manually alpha blending an RGBA pixel with an RGB pixel - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Mar 2019 07:01:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/alpha-blending-algorithm.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/alpha-blending-algorithm.html</guid>
        
        
        <category>algorithm</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用 Xamarin 开发 iOS 键盘扩展（含网络访问）</title>
        <description>&lt;p&gt;作为一位 .NET 技术的死忠，开发 iOS 应用当然要使用 Xamarin 啦！&lt;/p&gt;

&lt;p&gt;本文用我的阅读的文档和实践为素材，介绍如何使用 Xamarin 开发一个 iOS 的键盘扩展。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;你可以在 &lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard&quot;&gt;Walterlv.CloudKeyboard&lt;/a&gt; 仓库中获得本文所述的全部源代码。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;搭建环境&quot;&gt;搭建环境&lt;/h2&gt;

&lt;p&gt;本文不会花篇幅来讲如何搭建 Xamarin iOS 开发的环境，不然这篇文章就没有重点。&lt;/p&gt;

&lt;p&gt;于是，请阅读这一篇来了解如何搭建 Xamarin iOS 的开发环境：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;安装调试工具：Mac 部分 &lt;a href=&quot;https://www.jianshu.com/p/abb9ae9df631&quot;&gt;Xamarin开发(Mac开发)环境搭建 - 简书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;安装调试工具：Windows 部分 &lt;a href=&quot;https://blog.csdn.net/qq756288646/article/details/78967532&quot;&gt;vs2017开发IOS（vs2017 xamarin 连接mac） - ManGo.XYZ - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;申请开发者账号：&lt;a href=&quot;https://developer.apple.com/register/&quot;&gt;https://developer.apple.com/register/&lt;/a&gt;，&lt;a href=&quot;/post/tips-for-developing-xamarin-ios-app&quot;&gt;阅读这里了解坑&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;准备一根 Type-C 到 Lightning 的数据线，用于 Mac 从 Mac 部署到真机进行调试&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;你需要了解的-ios-键盘扩展的背景知识&quot;&gt;你需要了解的 iOS 键盘扩展的背景知识&lt;/h2&gt;

&lt;p&gt;了解以下背景知识，有助于我们接下来开发的时候少踩一些坑。&lt;em&gt;当然我不会在这里说 iOS 应用开发的所有背景知识，只会说与 iOS 键盘扩展相关的部分。&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;iOS 键盘扩展是 iOS 扩展的一种，而 iOS 扩展是 iOS 8.0 才开始引入的概念。&lt;/li&gt;
  &lt;li&gt;iOS 扩展需要有一个 iOS 普通应用作为容器一起打包；所以，你需要创建两个项目来完成 iOS 键盘扩展的开发。
    &lt;ul&gt;
      &lt;li&gt;在后文，我们将直接使用 iOS 容器应用来描述这个概念&lt;/li&gt;
      &lt;li&gt;扩展的包标识符（Bundle Identifier）必须以容器应用的包标识符字符串作为开头&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;iOS 扩展和 iOS 容器应用会被视为两款完全不同的应用，互相之前不能共享任何数据。
    &lt;ul&gt;
      &lt;li&gt;如果真的要共享数据，就需要像其他两款不同应用共享数据一样的处理方式&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;iOS 键盘扩展默认是不能访问网络的，你需要声明允许访问网络，并获得用户的同意才行。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;创建-ios-键盘扩展项目&quot;&gt;创建 iOS 键盘扩展项目&lt;/h2&gt;

&lt;h3 id=&quot;第一步创建-xamarinforms-项目&quot;&gt;第一步：创建 Xamarin.Forms 项目。&lt;/h3&gt;

&lt;p&gt;这个不用太在意里面的实现，因为它只是我们的“容器项目”（前面有介绍）。实际上在本文我们完全不会碰这个项目里面的代码，只是为了配置我们的 iOS 应用包而已。未来你可以在这个容器应用里面做键盘的个性化设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-19-43-40.png&quot; alt=&quot;创建 Xamarin.Forms 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，选择 iOS 平台。&lt;/p&gt;

&lt;p&gt;我们只需要 iOS 端。因为对于键盘，不同系统的实现差异很大，之间共享的代码只能是非键盘部分的代码了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-19-48-09.png&quot; alt=&quot;我们只选择 iOS 平台&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第二步创建-ios-键盘扩展项目&quot;&gt;第二步：创建 iOS 键盘扩展项目&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-19-50-52.png&quot; alt=&quot;创建新项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-19-57-10.png&quot; alt=&quot;创建 Custom Keyboard Extension 项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-19-58-16.png&quot; alt=&quot;创建完成之后的三个项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当你创建完之后，你会看到三个不同的项目。&lt;/p&gt;

&lt;p&gt;你可能发现 Walterlv.KeyboardExtension.Keyboard 项目有些奇怪，里面有 Main 函数和 AppDelegate，按道理这是一个主程序包。然而实际测试中单独有这个项目是跑不起来的（这可能是一个 Bug，如果修复了，请在下面评论或者邮件告知我，谢谢了）。&lt;/p&gt;

&lt;p&gt;于是，Main 和 AppDelegate 这两个文件是可以删除的。如果你强迫症，就删掉吧。当然不删掉也不影响，不过我删掉了。&lt;/p&gt;

&lt;h3 id=&quot;第三步引用-ios-键盘扩展项目&quot;&gt;第三步：引用 iOS 键盘扩展项目&lt;/h3&gt;

&lt;p&gt;在 iOS 容器应用上面添加键盘扩展项目作为引用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-20-04-39.png&quot; alt=&quot;在 iOS 容器应用上添加引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-20-05-35.png&quot; alt=&quot;引用键盘扩展项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你感兴趣去查看 Walterlv.KeyboardExtension.iOS 项目中对 Walterlv.KeyboardExtension.Keyboard 项目的引用节点的话，你会发现 Xamarin 已经自动为这个项目标记上了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsAppExtension /&amp;gt;&lt;/code&gt;。只有加上了 AppExtension 标记，Xamarin 才会把这个项目作为 iOS 扩展项目进行打包。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\..\Walterlv.KeyboardExtension.Keyboard\Walterlv.KeyboardExtension.Keyboard.csproj&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;{d6f006e7-3c98-4b97-b2d5-4d2e3bc2f945}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;Walterlv.KeyboardExtension.Keyboard&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsAppExtension&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsAppExtension&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsWatchApp&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsWatchApp&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ProjectReference&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在以上三个步骤完成之后，理论上你是可以正常编译此项目的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-20-10-58.png&quot; alt=&quot;可以编译通过&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;配置包信息&quot;&gt;配置包信息&lt;/h2&gt;

&lt;p&gt;iOS 应用的包信息存储在 plist 中。所以在这一节，你需要正确配置两个项目的 plist。&lt;/p&gt;

&lt;p&gt;没错！是两个项目。还记得前面背景知识里面我们说到容器项目和扩展项目就是两个不同的应用吗？&lt;/p&gt;

&lt;p&gt;配置 plist 的方法，就是在 Visual Studio 里面双击这个文件。&lt;/p&gt;

&lt;p&gt;按照下图这样配置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-21-02-57.png&quot; alt=&quot;配置两个项目的 plist 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说明：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Application Name 对应 plist 中的 CFBundleDisplayName 属性，也就是应用的显示名称。
    &lt;ul&gt;
      &lt;li&gt;对于容器应用，就是 iOS 图标下面的名称，对于键盘，就是切换键盘的时候所用的名称。&lt;/li&gt;
      &lt;li&gt;下图中 iOS 应用图标下面的名称 CloudKeyboard 就是我在 &lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard&quot;&gt;Walterlv.CloudKeyboard&lt;/a&gt; 项目中的容器应用的名称。&lt;/li&gt;
      &lt;li&gt;下图中在 iOS 切换键盘时，Cloud 就是我在 &lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard&quot;&gt;Walterlv.CloudKeyboard&lt;/a&gt; 项目中的键盘名称。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;扩展项目的 Bundle Identifier 名称必须以容器项目的 Bundle Identifier 名称作为前缀。
    &lt;ul&gt;
      &lt;li&gt;如果不满足要求，部署时扩展将不会生效。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-20-58-48.png&quot; alt=&quot;iOS 应用图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-20-56-47.png&quot; alt=&quot;iOS 上切换键盘&quot; /&gt;&lt;/p&gt;

&lt;p&gt;至此，你的项目可以直接编译了。如果你有真机部署环境，都可以直接部署到真机上看效果了。&lt;/p&gt;

&lt;h2 id=&quot;真机部署调试&quot;&gt;真机部署调试&lt;/h2&gt;

&lt;p&gt;本文不会花篇幅来讲如何真机部署调试，不然这篇文章就没有重点。&lt;/p&gt;

&lt;p&gt;但是你可以阅读：&lt;a href=&quot;/post/deploy-and-debug-ios-app-using-xamarin&quot;&gt;使用 Xamarin 在 iOS 真机上部署应用进行调试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;当然这是 Mac 版本的（毕竟我在 Windows 上实际也没有成功真机调试过，我是 git 同步到 Mac 上用 Visual Studio for Mac 来真机调试的）。&lt;/p&gt;

&lt;p&gt;只是你需要注意做这些内容：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你需要同意一份开发者证书（不然打不开应用）：
    &lt;ul&gt;
      &lt;li&gt;设置 -&amp;gt; 通用 -&amp;gt; 设备管理 -&amp;gt; [自己的开发者账号] -&amp;gt; 信任&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;还需要打开这个键盘（不然看不到键盘）：
    &lt;ul&gt;
      &lt;li&gt;设置 -&amp;gt; 通用 -&amp;gt; 键盘 -&amp;gt; 添加新键盘… -&amp;gt; [选择我们刚刚开发的键盘]&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面是我部署到真机上之后，在亮暗两种不同的界面下的键盘截图（就是上面的项目，没有改任何代码）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-22-07-21.png&quot; alt=&quot;键盘真机部署后的运行效果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;处理键盘的文字输入退格和确定&quot;&gt;处理键盘的文字输入、退格和确定&lt;/h2&gt;

&lt;p&gt;我们把 Walterlv.CloudKeyboard.iOS.Extension 也就是那个键盘扩展项目删除得只剩下 KeyboardViewController.cs 了，我们也只需要在这个类中写代码而已。&lt;/p&gt;

&lt;p&gt;要控制文字输入，就是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextDocumentProxy&lt;/code&gt; 实例。我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyboardViewController&lt;/code&gt; 继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIInputViewController&lt;/code&gt;，于是我们能够在类中直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextDocumentProxy&lt;/code&gt; 实例。&lt;/p&gt;

&lt;p&gt;在光标处插入文字：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TextDocumentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果要插入换行或者确认输入，则使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TextDocumentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在光标处删除前一个字：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TextDocumentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteBackward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果想要清空文本，则可以循环删除：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextDocumentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TextDocumentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeleteBackward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你没有办法删除后一个字，也不能获取到用户输入的任何内容。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;关于换行，特别注意：&lt;/strong&gt;如果文本框被设置为发送或者其他非换行的功能，那么使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;InsertText&lt;/code&gt; 单独插入换行时才能正常执行这些功能。如果调用此代码之前还有其他的插入文字，那么最终就只会是换行，而不会执行其他的功能。实际上我在这一点上踩了坑，导致在 QQ 或者其他工具中只能实现换行，而无法发送消息。&lt;/p&gt;

&lt;p&gt;iOS 的键盘有不同种类的确认，需要键盘针对 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextDocumentProxy.&lt;/code&gt;
我还没有找到办法直接完成文本的输入，例如执行确认按钮的逻辑。而确认按钮有这么些不同的情况：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 我当然是写 C# 语言版本的枚举，而不是 Object-C 版本的啦。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIReturnKeyType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Go&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Yahoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EmergencyCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加键盘的网络访问支持&quot;&gt;添加键盘的网络访问支持&lt;/h2&gt;

&lt;h3 id=&quot;允许完全访问包括网络&quot;&gt;允许完全访问（包括网络）&lt;/h3&gt;

&lt;p&gt;纯本地的键盘很难在打字速度上获得优势，各种主流的输入法也通常借助网络来提高自身的输入准确度。&lt;/p&gt;

&lt;p&gt;用户需要在键盘设置里面开启键盘的“允许完全访问”才能让对应的输入法获得网络访问的权限。如果用户没有给权限，那么网络访问的时候键盘扩展就会出现异常，然后闪退。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-21-26-33.png&quot; alt=&quot;允许完全访问&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而如果你去我们刚刚开发的输入法中看，你会发现我们的输入法没有提供这样的选项可以设置。那么如何能够添加这个设置以便进行网络访问呢？&lt;/p&gt;

&lt;p&gt;方法是修改键盘扩展项目的 Info.plist 文件。这个时候的修改，我们就不能使用 Visual Studio 中自带的 plist 编辑器了，我们需要使用文本编辑器来编辑 plist 文件。&lt;/p&gt;

&lt;p&gt;在你的 Info.plist 文件中找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;RequestsOpenAccess&lt;/code&gt; 属性，然后将它分值从 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;key&amp;gt;RequestsOpenAccess&amp;lt;/key&amp;gt;
&lt;span class=&quot;gd&quot;&gt;--  &amp;lt;false/&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  &amp;lt;true/&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个属性设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 之后，再次部署，你将可以在你的键盘设置里面看到“允许完全访问”的设置项。开启之后，你就能在你的键盘里面访问网络了。&lt;/p&gt;

&lt;h3 id=&quot;允许访问-http-不安全网络&quot;&gt;允许访问 http 不安全网络&lt;/h3&gt;

&lt;p&gt;一般来说你不用阅读这一小节的内容。因为现在基本上各种服务都已经是 https 了，http 基本已经绝迹。但是如果你需要临时部署一个服务，没来得及申请 https 证书的话，那么就需要使用本小结的内容让你的键盘支持 http 的访问。&lt;/p&gt;

&lt;p&gt;继续打开你的键盘扩展项目的 Info.plist 文件，在根字典的最后添加一个完整的字典属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;NSAppTransportSecurity&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSAppTransportSecurity&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSAllowsArbitraryLoads&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSExceptionDomains&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;walterlv.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSExceptionAllowsInsecureHTTPLoads&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSIncludesSubdomains&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;特别注意，里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.com&lt;/code&gt; 需要换成你自己的域名。是域名，不用包含端口号。&lt;/p&gt;

&lt;p&gt;这样，你就能在键盘中访问 &lt;a href=&quot;https://walterlv.com&quot;&gt;http://walterlv.com&lt;/a&gt; 了。&lt;/p&gt;

&lt;h2 id=&quot;本文总结&quot;&gt;本文总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;本文介绍了使用 Xamarin 开发 iOS 键盘插件的背景知识。
    &lt;ul&gt;
      &lt;li&gt;必须了解这些知识才不会在一些不太重要的坑上耗费太长时间。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;本文教大家如何开发 iOS 键盘插件，主要是项目组织以及写代码。
    &lt;ul&gt;
      &lt;li&gt;至少，使用文本编写出来的代码，能够在不作任何修改的情况下部署到真机。（实际上我们只在 KeyboardViewController.cs 中加了寥寥几行代码。）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;本文不涉及到搭建开发环境，不涉及如何连接真机调试。
    &lt;ul&gt;
      &lt;li&gt;你可能需要配合这些博客才能完成部署以及调试：&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/abb9ae9df631&quot;&gt;Xamarin开发(Mac开发)环境搭建 - 简书&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/qq756288646/article/details/78967532&quot;&gt;vs2017开发IOS（vs2017 xamarin 连接mac） - ManGo.XYZ - CSDN博客&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你还遇到了一些其他诡异的问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;欢迎阅读 &lt;a href=&quot;/post/tips-for-developing-xamarin-ios-app&quot;&gt;使用 Xamarin 开发 iOS 应用中需要注意的若干个问题&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;欢迎在评论区评论或者向我发邮件。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/xamarin/ios/platform/extensions&quot;&gt;iOS Extensions in Xamarin.iOS - Xamarin - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[iOS 8 Custom Keyboard Tutorial: How to Create A Third-Party Keyboard Extension&lt;/td&gt;
          &lt;td&gt;iPhone and iOS App UI Design Templates](http://www.appdesignvault.com/ios-8-custom-keyboard-extension/#a_aid=mdev)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/228e28bc5eab&quot;&gt;如何使用Xamarin开发iOS输入法 - 简书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42301827/make-http-request-from-custom-keyboard-app-extension&quot;&gt;ios - Make HTTP Request from Custom Keyboard App Extension - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http&quot;&gt;ios - Transport security has blocked a cleartext HTTP - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/0e345aec3689&quot;&gt;iOS - 输入框有值时才能点击键盘上的returnkey(enablesReturnKeyA… - 简书&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/25739312/handling-return-key-in-ios-8-keyboard-extension&quot;&gt;objective c - Handling Return key in iOS 8 keyboard extension - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/25739312/handling-return-key-in-ios-8-keyboard-extension&quot;&gt;objective c - Handling Return key in iOS 8 keyboard extension - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/4489879/ios-keyboard-with-go-button-instead-of-return&quot;&gt;iphone - iOS keyboard with “Go” button instead of return - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/ios/extensions/custom-keyboards/&quot;&gt;Custom Keyboards - Extensions - iOS - Human Interface Guidelines - Apple Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/swift-india/creating-a-custom-keyboard-in-ios-a75e7d5cc5ef&quot;&gt;Creating a Custom Keyboard In IOS… – Swift India – Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:15:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/develop-ios-keyboard-extension-using-xamarin.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/develop-ios-keyboard-extension-using-xamarin.html</guid>
        
        
        <category>xamarin</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>xaml</category>
        
        <category>ios</category>
        
      </item>
    
      <item>
        <title>如何为你的 Windows 应用程序关联一种或多种文件类型</title>
        <description>&lt;p&gt;对于 Windows 桌面应用来说，让应用关联一种或多种文件类型是通过修改注册表来实现的。&lt;/p&gt;

&lt;p&gt;本文介绍如何为你的应用关联自定义的文件类型或者关联被广泛使用的文件类型。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;文件关联&quot;&gt;文件关联&lt;/h2&gt;

&lt;p&gt;Windows 上的文件关联是通过文件的扩展名来实现的。有些文件类型是被广泛使用的公共类型，例如 .txt、.png、.mp4 文件；有些则是你自己的应用程序使用的私有类型，例如我自己定义一个 .lvyi 扩展名的文件类型。&lt;/p&gt;

&lt;p&gt;我们会关联这些广泛使用的类型可能是因为我们自己写了一个自己的文本编辑器，于是我们会关联 .txt 或者 .md 类型。而我们关联自定义的文件类型是因为我们需要为我们自己的应用生态产生一些文件数据。&lt;/p&gt;

&lt;p&gt;那么问题来了，我怎么知道我现在准备使用的扩展名是不是已经被广泛使用的公共类型呢？请进入此网站查看：&lt;a href=&quot;http://www.iana.org/assignments/media-types/media-types.xhtml&quot;&gt;Media Types&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;注册一个文件类型&quot;&gt;注册一个文件类型&lt;/h2&gt;

&lt;p&gt;要在 Windows 系统上注册一个文件类型，你需要做三个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;取一个应用程序标识符（&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/fa-progids&quot;&gt;ProgID&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;在注册表中添加文件关联（用于告知 Windows 这个文件已经被关联）&lt;/li&gt;
  &lt;li&gt;为关联的程序添加谓词（用于打开这个文件）&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;取一个应用程序标识符&quot;&gt;取一个应用程序标识符&lt;/h3&gt;

&lt;p&gt;没错，我说的就是取名字，而且要求在 Windows 系统上全局唯一；所以这里取名字也是有讲究的。关于应用程序标识符的相关内容，可以阅读微软的官方文档：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/fa-progids&quot;&gt;Programmatic Identifiers - Windows applications - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;微软建议的 ProgID 的取名方式是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;厂商名.应用名.版本号
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的版本号通常是指的大版本号。例如版本号为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.6.0.97&lt;/code&gt; 的应用，通常只取第一位，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;。一个典型的建议的取名示例是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Walterlv.Foo.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还是看微软自己的命名示例会更权威一点：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-20-10-03.png&quot; alt=&quot;来自微软的 ProgID 命名示例&quot; /&gt;&lt;/p&gt;

&lt;p&gt;竟然取一个名字也能写这么多篇幅，看来程序员的命名果然是世界上的一大难题呀！赶紧试用一下我的命名神器吧 —— &lt;a href=&quot;ms-windows-store://pdp/?productid=9P8LNZRNJX85&quot;&gt;点击下载&lt;/a&gt;，其原理可阅读 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名） - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;在注册表中添加文件关联&quot;&gt;在注册表中添加文件关联&lt;/h3&gt;

&lt;p&gt;你需要在注册表的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 添加一些子键：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_CURRENT_USER\Software\Classes
    .walv
        (Default) = Walterlv.Foo.1
    .lvyi
        (Default) = Walterlv.Foo.1
    Walterlv.Foo.1
        (Default) = 吕毅的示例文件
        Shell
            Open
                Command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; &quot;%1&quot;
                      
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.walv&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;lvyi&lt;/code&gt; 是我自己定义的两种文件类型，我将它们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(Default)&lt;/code&gt; 值设置成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Foo.1&lt;/code&gt;；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Foo.1&lt;/code&gt; 就是前面说的应用程序标识符（ProgID）。后面的又新建了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Foo.1&lt;/code&gt; 的键，其 &lt;code class=&quot;highlighter-rouge&quot;&gt;(Default)&lt;/code&gt; 值设置成了我们这个应用关联时使用的名称，也就是资源管理器中显示这个文件的时候使用的名称。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-20-51-54.png&quot; alt=&quot;在注册表中的 Walterlv.Foo.1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;只要我们完成了以上的步骤，我们就能在资源管理器中看到我们的文件关联（虽然双击打不开）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-03-14-05-37.png&quot; alt=&quot;在资源管理器中看到的文件关联&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;关于注册表路径的说明&lt;/strong&gt;：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE&lt;/code&gt; 主键是此计算机上的所有用户共享的注册表键值，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER&lt;/code&gt; 是当前用户使用的注册表键值。而我们在注册表的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 中也可以看到跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 中一样的文件关联项，是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 合并之后的一个视图，其中用户键值会覆盖此计算机上的相同键值。&lt;/p&gt;

&lt;p&gt;也就是说，如果你试图修改文件关联，那么需要去 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\Software\Classes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes&lt;/code&gt; 中，但如果只是去查看文件关联的情况，则只需要去 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CLASSES_ROOT&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;写入计算机范围内的注册表项需要管理员权限，而写入用户范围内的注册表项不需要管理员权限；你可以酌情选用。&lt;/p&gt;

&lt;h3 id=&quot;为关联的程序添加谓词&quot;&gt;为关联的程序添加谓词&lt;/h3&gt;

&lt;p&gt;我们需要为关联的程序添加谓词才能够使用我们的程序打开这个文件。通常进行文件关联时最常用的谓词是 &lt;code class=&quot;highlighter-rouge&quot;&gt;open&lt;/code&gt;，添加路径为 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Classes\Walterlv.Foo.1\shell\Open\Command&lt;/code&gt;。添加后，我们可以在文件资源管理器中通过双击打开这个文件。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Walterlv.Foo.1
    (Default) = 吕毅的示例文件
    shell
        Open
            Command
                (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; -f &quot;%1&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中路径后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;%1&quot;&lt;/code&gt; 是文件资源管理器传入的参数，其实就是文件的完整路径。我们加上了引号是避免解析命令行的时候把包含空格的路径拆成了多个参数。&lt;/p&gt;

&lt;p&gt;还可以添加其他谓词，有一些是预定义的谓词，你也可以随便写其他的谓词。另外，还可以定义文件的图标。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Walterlv.Foo.1
    (Default) = 吕毅的示例文件
    DefaultIcon = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\lvyi-icon.ico&quot;
    shell
        Open
            Command
                (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; open -f &quot;%1&quot;
        用逗比的方式打开
            Command
                (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; open -f &quot;%1&quot; --doubi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;反注册文件类型&quot;&gt;反注册文件类型&lt;/h2&gt;

&lt;p&gt;当你卸载你的程序的时候，需要反注册之前注册过的文件类型；而反注册的过程并不是把以上的过程完全反过来。&lt;/p&gt;

&lt;p&gt;微软推荐我们只删除 ProgID 的键，而不删除文件扩展名的键；因为其他的程序可能已经关联了我们的文件扩展名。就算我们使用的是私有的格式，也有可能是我们程序的未来版本会关联这个扩展名。&lt;/p&gt;

&lt;p&gt;总之，你需要做的，只是删除 ProgID 的键，文件扩展名的键不要去动它，Windows 自己会处理好 ProgID 删除之后文件关联的问题的。&lt;/p&gt;

&lt;h2 id=&quot;一个完整的文件关联示例&quot;&gt;一个完整的文件关联示例&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_CLASSES_ROOT
    .walv
        (Default) = Walterlv.Foo.1
    .lvyi
        (Default) = Walterlv.Foo.1
        Content Type = text/xml
    Walterlv.Foo.1
        (Default) = Walterlv Foo
        AlwaysShowExt = 1
        DefaultIcon = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\lvyi-icon.ico&quot;
        FriendlyTypeName = 吕毅的示例文件
        shell
            Open
                Command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; open -f &quot;%1&quot;
            用逗比的方式打开
                Command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; open -f &quot;%1&quot; --doubi
            Edit
                Command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; edit -f &quot;%1&quot;
            print
                command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; print -f &quot;%1&quot;
            printto
                command
                    (Default) = &quot;C:\Users\lvyi\AppData\Local\Walterlv.Foo\walterlv.exe&quot; print -f &quot;%1&quot; -t &quot;%2&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/fa-intro&quot;&gt;File Types and File Associations - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/fa-progids&quot;&gt;Programmatic Identifiers - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.iana.org/assignments/media-types/media-types.xhtml&quot;&gt;Media Types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:15:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-file-type-association.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-file-type-association.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程</title>
        <description>&lt;p&gt;MSBuild 的编译过程提供了一些可以被重写的 Target，通过重写这些 Target 可以扩展 MSBuild 的编译过程。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重写预定义的-target&quot;&gt;重写预定义的 Target&lt;/h2&gt;

&lt;p&gt;有这些预定义的 Target 可以重写：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeCompile&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterCompile&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeBuild&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeRebuild&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterRebuild&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeClean&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterClean&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforePublish&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterPublish&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeResolveReference&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterResolveReferences&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeResGen&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterResGen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你可以&lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;在 Microsoft.NET.Sdk 中找到各种富有创意的 Target 用来扩展&lt;/a&gt;，以上这些也是 Microsoft.NET.Sdk 的一部分，在那个文件夹的 Microsoft.Common.targets 或者 Microsoft.Common.CurrentVersion.targets 中。&lt;/p&gt;

&lt;p&gt;而写法是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    ...
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeResGen&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里可以写在生成资源之前执行的 Task 或者修改属性和集合。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AfterCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这里可以写在 C# 文件以及各种资源文件编译之后执行的 Task 或者修改属性和集合。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是的，相比于你全新定义一个 Target 来说，你不需要去写 BeforeTargets 或者 AfterTargets。&lt;/p&gt;

&lt;p&gt;那么以上那些 Target 都是什么时机呢？&lt;/p&gt;

&lt;h3 id=&quot;beforecompile-aftercompile&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeCompile&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterCompile&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在 C# 文件以及各种资源文件被编译成 dll 的之前或之后执行。你可以在之前执行以便修改要编译的 C# 文件或者资源文件，你也可以在编译之后做一些其他的操作。&lt;/p&gt;

&lt;p&gt;由于我们可以在 BeforeCompile 这个时机修改源码，所以我们很多关于代码级别的重新定义都可以在这个时机去完成。&lt;/p&gt;

&lt;h3 id=&quot;beforebuild-afterbuild&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeBuild&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在整个编译之前或者之后执行。对于普通的编译来说，一般来说不会有比 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeBuild&lt;/code&gt; 更前以及比 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterBuild&lt;/code&gt; 更后的时机了，不过如果有其他 Import 进来的 Target 或者通过 NuGet 自动引入进来的其他 Target 也使用了类似这样的时机，那么你就不一定比他们更靠前或者靠后。&lt;/p&gt;

&lt;h3 id=&quot;beforerebuild-afterrebuild&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeRebuild&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterRebuild&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;如果编译时采用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;/t:Rebuild&lt;/code&gt; 方案，也就是重新编译，那么 BeforeRebuild 和 AfterRebuild 就会被触发。一旦触发，会比前面更加提前和靠后。&lt;/p&gt;

&lt;p&gt;执行顺序为：BeforeRebuild -&amp;gt; Clean -&amp;gt; Build -&amp;gt; AfterRebuild&lt;/p&gt;

&lt;h3 id=&quot;beforeclean-afterclean&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeClean&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterClean&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在清理开始和结束时执行。如果是重新编译，那么也会有 Clean 的过程。顺序见上面。&lt;/p&gt;

&lt;h3 id=&quot;beforepublish-afterpublish&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforePublish&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterPublish&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在发布之前执行和发布之后执行。对应到 Visual Studio 右键菜单中的发布按钮。&lt;/p&gt;

&lt;h3 id=&quot;beforeresolvereference-afterresolvereferences&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeResolveReference&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterResolveReferences&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在程序集的引用被解析之前和之后执行。你可以通过重写这两个时机的 Target 来修改程序集的引用关系或者利用引用执行一些其他操作。&lt;/p&gt;

&lt;h3 id=&quot;beforeresgen-afterresgen&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeResGen&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterResGen&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;在资源被生成之前和之后执行。&lt;/p&gt;

&lt;h2 id=&quot;通过改写-dependson-的值扩展编译&quot;&gt;通过改写 DependsOn 的值扩展编译&lt;/h2&gt;

&lt;p&gt;有这些预定义的 DependsOn 可以改写：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BuildDependsOn&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CleanDependsOn&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CompileDependsOn&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这几个属性的时机跟上面是一样的，你可以直接通过阅读上面一节中对应名字的 Target 的解释来获得这几个属性所对应的时机。&lt;/p&gt;

&lt;p&gt;而这几个属性影响编译过程的写法是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildDependsOn&amp;gt;&lt;/span&gt;WalterlvDemoTarget1;$(BuildDependsOn);WalterlvDemoTarget1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildDependsOn&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;正在运行 WalterlvDemoTarget1……&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;正在运行 WalterlvDemoTarget2……&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更推荐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOn&lt;/code&gt; 属性的改写而不是像本文第一节那样直接重写 Target，是因为一个 Target 的重写很容易被不同的开发小伙伴覆盖。比如一个小伙伴在一处代码里面写了一个 Target，但另一个小伙伴不知道，在另一个地方也写了相同名字的 Target，那么这两个 Target 也会相互覆盖，导致其中的一个失效。&lt;/p&gt;

&lt;p&gt;虽然同名的属性跟 Target 一样的会被覆盖，但是我们可以通过在改写属性的值的时候同时获取这个属性之前设置的值，可以把以前的值保留下来。&lt;/p&gt;

&lt;p&gt;正如上面的例子那样，我们通过写了两个新的 Target 的名字，分别叠加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(BuildDependsOn)&lt;/code&gt; 这个属性原有值的两边，使得我们可以在编译前后执行两个不同的 Target。如果有其他的小伙伴使用了相同的方式去改写这个属性的值，那么它获取原有值的时候就会把这里已经赋过的值放入到它新的值的中间。也就是说，一个也不会丢。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-extend-the-visual-studio-build-process&quot;&gt;Extend the build process - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/11667510/determine-if-msbuild-corecompile-will-run-and-call-custom-target&quot;&gt;c# - Determine if MSBuild CoreCompile will run and call custom target - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:15:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/extend-the-visual-studio-build-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/extend-the-visual-studio-build-process.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>在 Target 中获取项目引用的所有依赖（dll/NuGet/Project）的路径</title>
        <description>&lt;p&gt;在项目编译成 dll 之前，如何分析项目的所有依赖呢？可以在在项目的 Target 中去收集项目的依赖。&lt;/p&gt;

&lt;p&gt;本文将说明如何在 Target 中收集项目依赖的所有 dll 的文件路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写-target&quot;&gt;编写 Target&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;References:&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Reference)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 Target 的作用是将项目的所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Reference&lt;/code&gt; 节点作为集合输出出来。然而实际上如果真的编译这个项目，会发现我们得到的结果有一些问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;实际上其值就是写到每一个 Reference 里面的字符串的集合
    &lt;ul&gt;
      &lt;li&gt;比如引用了 System.Xaml，那么这里就会是 System.Xaml&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如果引用是通过 ProjectReference 进行的项目引用，那么这里就没有目标项目的 dll&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，我们需要一个新的属性来查找引用的 dll。通过 &lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;研究 Microsoft.NET.Sdk 的源码&lt;/a&gt;，我发现有 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReferencePath&lt;/code&gt; 属性可以使用，于是将 Target 改为这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile;ResolveAssemblyReference&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferencePaths:&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(ReferencePath)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在得到的所有依赖字符串则没有以上的问题。&lt;/p&gt;

&lt;p&gt;注意，我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 上增加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResolveAssemblyReference&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;以上-target-的输出&quot;&gt;以上 Target 的输出&lt;/h2&gt;

&lt;p&gt;引用通常很多，所以我将以上的输出单独放到这里来，避免影响到上面一节知识的阅读。&lt;/p&gt;

&lt;h3 id=&quot;reference-的输出&quot;&gt;Reference 的输出&lt;/h3&gt;

&lt;p&gt;可以看到，Reference 的输出几乎就是 Reference 中写的字符串本身。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CefSharp, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86
CefSharp.Core, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86
CefSharp.WinForms, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86
Microsoft.Expression.Interactions, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL
System.IO.Compression.FileSystem
System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL
WindowsFormsIntegration
C:\Users\walterlv\.nuget\packages\walterlv.demopackage\1.0.0.0\lib\net47\Walterlv.DemoPackageLibrary.dll
PresentationCore
System.ComponentModel.Composition
System.Configuration
System.Windows.Forms
WindowsBase
PresentationFramework
System.Xaml
System.ServiceModel
System
System.Data
System.Data.DataSetExtensions
System.Management
System.Net.Http
System.Runtime.Serialization
System.ServiceProcess
System.Web
System.Xml
System.Xml.Linq
System.Drawing
Microsoft.CSharp
System.Core
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;referencepath-的输出&quot;&gt;ReferencePath 的输出&lt;/h3&gt;

&lt;p&gt;可以看到，ReferencePath 则是将所有的 dll 的路径也输出了，而且即便是项目引用，项目编译好的 dll 的路径也在。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;D:\Walterlv\Demo\Walterlv.Demo\Code\_Externals\Refs\Cef\x86\CefSharp.Core.dll
D:\Walterlv\Demo\Walterlv.Demo\Code\_Externals\Refs\Cef\x86\CefSharp.dll
D:\Walterlv\Demo\Walterlv.Demo\Code\_Externals\Refs\Cef\x86\CefSharp.WinForms.dll
C:\Users\walterlv\.nuget\packages\walterlv.demopackage\1.0.0.0\lib\net47\Walterlv.DemoPackageLibrary.dll
D:\Walterlv\Demo\Walterlv.Demo\Walterlv.Library1\bin\Debug\Walterlv.Library1.dll
D:\Walterlv\Demo\Walterlv.Demo\Walterlv.Library2\bin\Debug\Walterlv.Library2.dll
D:\Walterlv\Demo\Walterlv.Demo\Walterlv.Library3\bin\Debug\Walterlv.Library3.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Microsoft.CSharp.dll
D:\Walterlv\Demo\Walterlv.Demo\Code\_Externals\Refs\Microsoft.Expression.Interactions.dll
C:\Users\walterlv\.nuget\packages\windowsapicodepackshell\1.1.0.8\lib\NET45\Microsoft.WindowsAPICodePack.dll
C:\Users\walterlv\.nuget\packages\windowsapicodepackshell\1.1.0.8\lib\NET45\Microsoft.WindowsAPICodePack.Shell.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll
C:\Users\walterlv\.nuget\packages\newtonsoft.json\11.0.2\lib\net45\Newtonsoft.Json.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\PresentationCore.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\PresentationFramework.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ComponentModel.Composition.dll
C:\Users\walterlv\.nuget\packages\system.composition.attributedmodel\1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll
C:\Users\walterlv\.nuget\packages\system.composition.convention\1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll
C:\Users\walterlv\.nuget\packages\system.composition.hosting\1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll
C:\Users\walterlv\.nuget\packages\system.composition.runtime\1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll
C:\Users\walterlv\.nuget\packages\system.composition.typedparts\1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Configuration.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Core.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.DataSetExtensions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll
C:\Users\walterlv\.nuget\packages\system.data.sqlite.core\1.0.97\lib\net45\System.Data.SQLite.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Drawing.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.IO.Compression.FileSystem.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Management.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Net.Http.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Runtime.Serialization.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ServiceModel.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ServiceProcess.dll
C:\Users\walterlv\.nuget\packages\system.valuetuple\4.5.0\ref\portable-net40+sl4+win8+wp8\System.ValueTuple.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Web.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Windows.Forms.dll
D:\Walterlv\Demo\Walterlv.Demo\Code\_Externals\Refs\System.Windows.Interactivity.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xaml.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.Linq.dll
C:\Users\walterlv\.nuget\packages\texteditorplus\1.0.0.903\lib\NET45\TextEditorPlus.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\WindowsBase.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\WindowsFormsIntegration.dll
C:\Users\walterlv\.nuget\packages\wpfmediakit\3.0.2.78\lib\NET45\WPFMediaKit.dll
obj\Debug\Interop.IWshRuntimeLibrary.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Collections.Concurrent.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Collections.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ComponentModel.Annotations.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ComponentModel.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ComponentModel.EventBasedAsync.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Diagnostics.Contracts.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Diagnostics.Debug.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Diagnostics.Tools.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Diagnostics.Tracing.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Dynamic.Runtime.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Globalization.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Linq.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Linq.Expressions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Linq.Parallel.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Linq.Queryable.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Net.NetworkInformation.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Net.Primitives.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Net.Requests.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ObjectModel.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.Emit.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.Emit.ILGeneration.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.Emit.Lightweight.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.Extensions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Reflection.Primitives.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Resources.ResourceManager.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.Extensions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.Numerics.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.Serialization.Json.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.Serialization.Primitives.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.Serialization.Xml.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Security.Principal.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ServiceModel.Duplex.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ServiceModel.Http.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ServiceModel.NetTcp.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ServiceModel.Primitives.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.ServiceModel.Security.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Text.Encoding.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Text.Encoding.Extensions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Text.RegularExpressions.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.Parallel.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Xml.ReaderWriter.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Xml.XDocument.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Xml.XmlSerializer.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;解读原因&quot;&gt;解读原因&lt;/h2&gt;

&lt;p&gt;解析引用的 dll 的路径的 Task 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResolveAssemblyReference&lt;/code&gt;，你可以在 &lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;Microsoft.NET.Sdk 文件夹&lt;/a&gt; 中找到它。如果想知道 Task 是什么意思，可以阅读：&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ResolveAssemblyReference&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Assemblies=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Reference)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;AssemblyFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ResolvedProjectReferencePaths);@(_ExplicitReference)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetFrameworkDirectories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ReferenceInstalledAssemblyDirectory)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;InstalledAssemblyTables=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(InstalledAssemblyTables);@(RedistList)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;IgnoreDefaultInstalledAssemblyTables=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IgnoreDefaultInstalledAssemblyTables)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;IgnoreDefaultInstalledAssemblySubsetTables=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IgnoreInstalledAssemblySubsetTables)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;CandidateAssemblyFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Content);@(None)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;SearchPaths=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AssemblySearchPaths)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;AllowedAssemblyExtensions=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AllowedReferenceAssemblyFileExtensions)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;AllowedRelatedFileExtensions=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AllowedReferenceRelatedFileExtensions)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetProcessorArchitecture=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ProcessorArchitecture)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;AppConfigFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ResolveAssemblyReferencesApplicationConfigFileForExes)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;AutoUnify=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(AutoUnifyAssemblyReferences)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;SupportsBindingRedirectGeneration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(GenerateBindingRedirectsOutputType)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;IgnoreVersionForFrameworkReferences=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IgnoreVersionForFrameworkReferences)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FindDependencies=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_FindDependencies)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FindSatellites=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BuildingProject)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FindSerializationAssemblies=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BuildingProject)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FindRelatedFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BuildingProject)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Silent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ResolveAssemblyReferencesSilent)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetFrameworkVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFrameworkVersion)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetFrameworkMoniker=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFrameworkMoniker)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetFrameworkMonikerDisplayName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFrameworkMonikerDisplayName)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetedRuntimeVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetedRuntimeVersion)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;StateFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ResolveAssemblyReferencesStateFile)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;InstalledAssemblySubsetTables=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(InstalledAssemblySubsetTables)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;TargetFrameworkSubsets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_ReferenceInstalledAssemblySubsets)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FullTargetFrameworkSubsetNames=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(FullReferenceAssemblyNames)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FullFrameworkFolders=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(_FullFrameworkReferenceAssemblyPaths)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FullFrameworkAssemblyTables=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(FullFrameworkAssemblyTables)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ProfileName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(TargetFrameworkProfile)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;LatestTargetFrameworkDirectories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(LatestTargetFrameworkDirectories)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;CopyLocalDependenciesWhenParentReferenceInGac=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(CopyLocalDependenciesWhenParentReferenceInGac)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DoNotCopyLocalIfInGac=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(DoNotCopyLocalIfInGac)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ResolvedSDKReferences=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(ResolvedSDKReference)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;WarnOrErrorOnTargetArchitectureMismatch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;IgnoreTargetFrameworkAttributeVersionMismatch =&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ResolveAssemblyReferenceIgnoreTargetFrameworkAttributeVersionMismatch)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FindDependenciesOfExternallyResolvedReferences=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(FindDependenciesOfExternallyResolvedReferences)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ContinueOnError=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(ContinueOnError)&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'@(Reference)'!='' or '@(_ResolvedProjectReferencePaths)'!='' or '@(_ExplicitReference)' != ''&quot;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResolvedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferencePath&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResolvedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ResolveAssemblyReferenceResolvedFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResolvedDependencyFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceDependencyPaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RelatedFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ReferenceRelatedPaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SatelliteFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceSatellitePaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SerializationAssemblyFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ReferenceSerializationAssemblyPaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScatterFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ReferenceScatterPaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CopyLocalFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReferenceCopyLocalPaths&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SuggestedRedirects&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SuggestedBindingRedirects&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FilesWritten&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FileWrites&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DependsOnSystemRuntime&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DependsOnSystemRuntime&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DependsOnNETStandard&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_DependsOnNETStandard&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResolveAssemblyReference&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从这个 Task 中可以看出，它还输出了以下这些属性或集合：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ReferenceDependencyPaths&lt;/li&gt;
  &lt;li&gt;ReferenceSatellitePaths&lt;/li&gt;
  &lt;li&gt;ReferenceCopyLocalPaths
    &lt;ul&gt;
      &lt;li&gt;这是需要拷贝到本地的那些 dll 的路径（不含框架自带的 dll）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SuggestedBindingRedirects&lt;/li&gt;
  &lt;li&gt;FileWrites
    &lt;ul&gt;
      &lt;li&gt;要写入的一些缓存文件&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;DependsOnSystemRuntime
    &lt;ul&gt;
      &lt;li&gt;以上都是集合，唯独这是一个布尔值，表示是否依赖系统运行时&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:15:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/resolve-project-references-using-target.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/resolve-project-references-using-target.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>如何在命令行中监听用户输入文本的改变？</title>
        <description>&lt;p&gt;这真是一个诡异的需求。为什么我需要在命令行中得知用户输入文字的改变啊！实际上我希望实现的是：在命令行中输入一段文字，然后不断地将这段文字发往其他地方。&lt;/p&gt;

&lt;p&gt;本文将介绍如何监听用户在命令行中输入文本的改变。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在命令行中输入有三种不同的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Console.Read()&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用户可以一直输入，在用户输入回车之前，此方法都会一直阻塞。而一旦用户输入了回车，你后面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.Read&lt;/code&gt; 就不会一直阻塞了，直到把用户在这一行输入的文字全部读完。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadKey()&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用户输入之前此方法会一直阻塞，用户只要按下任何一个键这个方法都会返回并得到用户按下的按键信息。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadLine()&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用户可以一直输入，在用户输入回车之前，此方法都会一直阻塞。当用户输入了回车之后，此方法会返回用户在这一行输入的字符串。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;从表面上来说，以上这三个方法都不能满足我们的需求，每一个方法都不能直接监听用户的输入文本改变。尤其是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.Read()&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadLine()&lt;/code&gt; 方法，在用户输入回车之前，我们都得不到任何信息。看起来我们似乎只能通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadKey()&lt;/code&gt; 来完成我们的需求了。&lt;/p&gt;

&lt;p&gt;但是，一旦我们使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadKey()&lt;/code&gt;，我们将不能获得另外两个方法中的输入体验。例如，我们按下退格键（BackSpace）可以删除光标的前一个字符，按下删除键（Delete）可以删除光标的后一个字符，按下左右键可以移动光标到合适的文本上。&lt;/p&gt;

&lt;p&gt;然而，不幸的是，除了这三个方法，我们还真的没有原生的方法来实现命令行的输入监听了。所以看样子我们需要自己来使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadKey()&lt;/code&gt; 实现用户输入文字的监听了。&lt;/p&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/input-password-with-mask-in-cli&quot;&gt;如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文 - walterlv&lt;/a&gt; 一问中有说到如何在命令行中输入密码而不会显示明文。我们用到的就是此博客中所述的方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 用户在这里输入了回车，于是我们需要结束输入了。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Backspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\b \b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而实际上在使用此方法的时候并不符合预期，因为退格的时候我们得到了半个字：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-21-56-04.png&quot; alt=&quot;我们得到了半个字&quot; /&gt;&lt;/p&gt;

&lt;p&gt;额外的，我们还不支持左右键移动光标，而且按住控制键的时候也会输入一个字符；这些都是我还没有处理的。&lt;/p&gt;

&lt;p&gt;这就意味着我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;\b \b&quot;&lt;/code&gt; 来删除我们输入的字符的时候，有可能在一些字符的情况下我们需要删除两个字符宽度。&lt;/p&gt;

&lt;p&gt;然而如何获取一个字的字符宽度呢？还是很复杂的。于是我很暴力地使用 &lt;a href=&quot;https://bbs.csdn.net/topics/390088904&quot;&gt;OnChar函数的中文处理问题，退格键时，怎么处理-CSDN论坛&lt;/a&gt; 论坛中使用的方法直接通过编码范围判断中文的方式来推测字符宽度。如果你有更正统的方法，非常欢迎指导我。&lt;/p&gt;

&lt;p&gt;简单起见，我写了一个类来封装输入文本改变。阅读以下代码，或者访问 &lt;a href=&quot;https://github.com/walterlv/Walterlv.CloudKeyboard/blob/master/CloudKeybaord.Cli/ConsoleLineReader.cs&quot;&gt;Walterlv.CloudKeyboard/ConsoleLineReader.cs&lt;/a&gt; 阅读此类型的最新版本的代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsoleLineReader&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsoleTextChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;OnTextChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Backspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastChar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastChar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xA0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\b\b  \b\b&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\b \b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;nf&quot;&gt;OnTextChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnTextChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TextChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConsoleTextChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsoleTextChangedEventArgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConsoleTextChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么使用的时候，则会简单很多：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConsoleLineReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里可以在用户每次输入的文本改变的时候执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 我在这里循环执行，于是即便用户按了回车，也会继续输入。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/StreamReader.cs,ef2abdf7bd65b2ec&quot;&gt;StreamReader.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/questions/863031/how-to-backspace-the-characters-in-the-cmd-buffer&quot;&gt;windows - How to backspace the characters in the cmd buffer? - Super User&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.console.keyavailable&quot;&gt;Console.KeyAvailable Property (System) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://bbs.csdn.net/topics/390088904&quot;&gt;OnChar函数的中文处理问题，退格键时，怎么处理-CSDN论坛&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:14:42 +0000</pubDate>
        <link>https://blog.walterlv.com/post/notify-text-changed-when-typing-in-console-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/notify-text-changed-when-typing-in-console-application.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>仅反射加载（ReflectionOnlyLoadFrom）的 .NET 程序集，如何反射获取它的 Attribute 元数据呢？</title>
        <description>&lt;p&gt;平时我们获取一个程序集或者类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 是非常轻松的，只需要通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 方法就能拿到实例然后获取其中的值。但是，有时我们仅为反射加载一些程序集的时候，获取这些元数据就不那么简单了，因为我们没有加载目标程序集中的类型。&lt;/p&gt;

&lt;p&gt;本文介绍如何为仅反射加载的程序集读取 Attribute 元数据信息。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;仅反射加载一个程序集&quot;&gt;仅反射加载一个程序集&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReflectionOnlyLoadFrom&lt;/code&gt; 可以仅以反射的方式加载一个程序集。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\walterlv\Desktop\Walterlv.Extension.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReflectionOnlyLoadFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;获取程序集的-attribute例如获取程序集版本号&quot;&gt;获取程序集的 Attribute（例如获取程序集版本号）&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetCustomAttributesData()&lt;/code&gt; 得到的是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomAttributeData&lt;/code&gt; 的列表，而这个列表中的每一项都与普通反射中拿到的特性集合不同，这里拿到的只是特性的信息（以下循环中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; 变量）。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CustomAttributeData&lt;/code&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;AttributeType&lt;/code&gt; 属性，虽然此属性是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type&lt;/code&gt; 类型的，但是实际上它只会是 &lt;code class=&quot;highlighter-rouge&quot;&gt;RuntimeType&lt;/code&gt; 类型，而不会是真实的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 的类型（因为不能保证宿主程序域中已经加载了那个类型）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customAttributesData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CustomAttributeData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里可以针对每一个拿到的慝的信息进行操作。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如我们要获取这个程序集的版本号，正常我们写 &lt;code class=&quot;highlighter-rouge&quot;&gt;assembly.GetCustomAttribute&amp;lt;AssemblyFileVersionAttribute&amp;gt;().Version&lt;/code&gt;，但是这里我们无法生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyFileVersionAttribute&lt;/code&gt; 的实例，我们只能这么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;versionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssemblyFileVersionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructorArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;versionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;代码解读是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们从拿到的所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 元数据中找到第一个名称与 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyFileVersionAttribute&lt;/code&gt; 相同的数据；&lt;/li&gt;
  &lt;li&gt;从数据的构造函数参数中找到传入的参数值，而这个值就是我们定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyFileVersionAttribute&lt;/code&gt; 时传入的参数的实际值。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因为我们知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyFileVersionAttribute&lt;/code&gt; 的构造函数只有一个，所以我们确信可以从第一个参数中拿到我们想要的值。&lt;/p&gt;

&lt;p&gt;顺便一提，我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyFileVersionAttribute&lt;/code&gt; 而不是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyVersionAttribute&lt;/code&gt; 是因为使用 .NET Core 新格式（基于 Microsoft.NET.Sdk）编译出来的程序集默认是不带 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyVersionAttribute&lt;/code&gt; 的。详见：&lt;a href=&quot;/post/semantic-version&quot;&gt;语义版本号（Semantic Versioning） - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata&quot;&gt;CustomAttributeData Class (System.Reflection) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/1459565/6233938&quot;&gt;c# - How to get custom attributes from an assembly that is not (really) loaded - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/37420518/6233938&quot;&gt;c# - Get custom attribute data from assembly file and unlock it afterwise - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 14:14:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-attributes-for-reflection-only-loaded-assembly.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-attributes-for-reflection-only-loaded-assembly.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 Visual Studio 自定义外部命令 (External Tools) 快速打开 git bash 等各种工具</title>
        <description>&lt;p&gt;Visual Studio 支持自定义的外部命令，于是即便 Visual Studio 原生没有的功能，插件没有提供的功能，你也能仅仅通过配置就实现。比如，我们可以一键打开 Git Bash 输入 git 命令，比如可以一键打开项目或者文件所在的文件夹。&lt;/p&gt;

&lt;p&gt;本文将教你如何自定义 Visual Studio 的外部命令，并提供一些我自己正在使用的外部命令配置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-13-02.png&quot; alt=&quot;在 Visual Studio 中的外部命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看，就是一键的按钮！&lt;/p&gt;

&lt;p&gt;所以，你想不想也在 Visual Studio 的工具栏上增加高效率的功能按钮呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一键打开 Git Bash&lt;/li&gt;
  &lt;li&gt;一键打开解决方案所在文件夹&lt;/li&gt;
  &lt;li&gt;一键 Blame 正在打开的文件 &lt;em&gt;(话说 VS17 的 Blame 功能也没好到哪儿去，还是得 TortoiseGit)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们开始吧！&lt;/p&gt;

&lt;h2 id=&quot;第一步自定义外部命令&quot;&gt;第一步：自定义外部命令&lt;/h2&gt;

&lt;p&gt;打开 [工具] -&amp;gt; [外部命令]，然后在新打开的对话框中编辑外部命令。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-17-11.png&quot; alt=&quot;自定义外部命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了方便，我把我自己正在用的几个外部命令分享给大家：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;用于一键打开 Git Bash，以便快速输入 git 命令
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;打开 Git Bash&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\Git\git-bash.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;--cd=&quot;$(SolutionDir)\.&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(SolutionDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;&lt;!-- 1. 用于一键打开 Git Bash，以便快速输入 git 命令
     - [Title] `打开 Git Bash`
     - [Command] `C:\Program Files\Git\usr\bin\mintty.exe`
     - [Arguments] `--nodaemon -o AppID=GitForWindows.Bash -o AppLaunchCmd=&quot;C:\Program Files\Git\git-bash.exe&quot; -o AppName=&quot;Git Bash&quot; -i &quot;C:\Program Files\Git\git-bash.exe&quot; --store-taskbar-properties -- /usr/bin/bash --login -i`
     - [InitialDirectory] `&quot;$(SolutionDir)&quot;` --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;用于快速打开解决方案所在的文件夹（通常这也是 git 仓库的根目录）
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;在资源管理器中查看此解决方案&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\explorer.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;/select,&quot;$(SolutionDir)$(SolutionFileName)&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(SolutionDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;用于快速打开当前正在编辑的文件所在的文件夹
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;在资源管理器中查看此文件&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\explorer.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;/select,&quot;$(ItemPath)&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(ItemDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;使用 VSCode 打开此解决方案（这可以用来快速编辑某些 VS 中不方便编辑的文件）
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;使用 VSCode 编辑&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%\Programs\Microsoft VS Code\Code.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(SolutionDir)&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(SolutionDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;使用 TortoiseGit 来 Blame 此文件（而且还会自动定位到当前行）
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;追溯此文件&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\TortoiseGit\bin\TortoiseGitBlame.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(ItemPath)&quot; /line:$(CurLine)&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(ItemDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;使用 TortoiseGit 来查看此文件的 git 日志
    &lt;ul&gt;
      &lt;li&gt;[Title] &lt;code class=&quot;highlighter-rouge&quot;&gt;查看此文件的历史记录&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Command] &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[Arguments] &lt;code class=&quot;highlighter-rouge&quot;&gt;/command:log /path:&quot;$(ItemPath)&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;[InitialDirectory] &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;$(ItemDir)&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;第二步自定义工具条按钮&quot;&gt;第二步：自定义工具条按钮&lt;/h2&gt;

&lt;p&gt;点击工具条最右侧的小箭头，然后添加删除按钮，在长长的下拉框的最后，有一个“自定义”菜单项。打开，我们接下来的操作都在这里面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-36-57.png&quot; alt=&quot;添加或删除按钮&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后，按照下图操作添加一个外部命令。注意，外部命令的序号从 1 开始，就是我们在上一节外部命令框中那些命令的序号。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-41-32.png&quot; alt=&quot;添加一个外部命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后，编辑这个外部命令。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-43-42.png&quot; alt=&quot;编辑这个外部命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在一个个添加完成之后，Visual Studio 的顶部工具栏中就会出现我们刚刚添加的各种外部命令了。点击可以一键使用相应的功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-02-10-52-26.png&quot; alt=&quot;添加并编辑完的外部命令&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 04 Mar 2019 03:06:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/customize-external-tools-for-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/customize-external-tools-for-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>让你的 Windows 应用程序在任意路径也能够直接通过文件名执行</title>
        <description>&lt;p&gt;我们可以在任何路径下输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;explorer&lt;/code&gt; 来启动资源管理器，可以在任何路径中输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt; 来使用 git 相关的命令。我们知道可以通过将一个应用程序加入到环境变量中来获得这个效果，但是还有其他的方式吗？&lt;/p&gt;

&lt;p&gt;我们将这个过程称之为向 Windows 注册一个应用程序路径。本文介绍向 Windows 注册一个应用程序路径的各种方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-如何查找程序路径&quot;&gt;Windows 如何查找程序路径？&lt;/h2&gt;

&lt;p&gt;当我们在任意目录中输入一个命令的时候，Windows 会按照如下顺序寻找这个命令对应的可执行程序：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当前的工作目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows&lt;/code&gt; 文件夹（仅此文件夹，不会搜索子文件夹）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows\System32&lt;/code&gt; 文件夹&lt;/li&gt;
  &lt;li&gt;环境变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 值中的所有文件夹&lt;/li&gt;
  &lt;li&gt;注册表 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;微软 &lt;strong&gt;推荐使用&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;App Paths&lt;/code&gt; 即修改此注册表项来添加可执行程序。&lt;/p&gt;

&lt;p&gt;当然，你也可以使用当前用户键下的注册表项来实现同样的目的，程序使用当前用户路径写注册表是不需要管理员权限的。&lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用-app-paths-添加可执行程序&quot;&gt;使用 App Paths 添加可执行程序&lt;/h2&gt;

&lt;p&gt;在注册表中打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths&lt;/code&gt; 子键，你可以在里面找到当前通过此方法注册的所有可执行程序。&lt;/p&gt;

&lt;p&gt;比如下图是 PowerShell Core 的 msi 包安装后添加的 &lt;code class=&quot;highlighter-rouge&quot;&gt;pwsh.exe&lt;/code&gt; 键。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-17-31-37.png&quot; alt=&quot;PowerShell Core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在我们添加一个我们自己开发的程序 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.exe&lt;/code&gt;，于是就直接在 &lt;code class=&quot;highlighter-rouge&quot;&gt;App Paths&lt;/code&gt; 子键下添加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.exe&lt;/code&gt; 的键，并将其默认值设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv.exe&lt;/code&gt; 的完整路径。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-02-19-02-35.png&quot; alt=&quot;新增的 walterlv.exe&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/shell/app-registration&quot;&gt;Application Registration - Windows applications - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 02 Mar 2019 11:07:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/run-your-application-without-full-executable-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/run-your-application-without-full-executable-path.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>编写 MSBuild 内联编译任务（Task）用于获取当前编译环境下的所有编译目标（Target）</title>
        <description>&lt;p&gt;我之前写过一些改变 MSBuild 编译过程的一些博客，包括利用 Microsoft.NET.Sdk 中各种自带的 Task 来执行各种各样的编译任务。更复杂的任务难以直接利用自带的 Task 实现，需要自己写 Task。&lt;/p&gt;

&lt;p&gt;本文将编写一个内联的编译任务，获取当前编译环境下的所有编译目标（Target）。获取所有的这些 Target 对我们调试一些与 MSBuild 或编译相关的问题时可能带来一些帮助。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;编写纯 C# 版本编译任务获取所有编译目标（Target）的代码是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Evaluation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Build.Framework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvGetAllTargets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ITaskItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvTargets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProjectFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskItems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ITaskItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyValuePair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectTargetInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Condition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Inputs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inputs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Outputs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Outputs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DependsOnTargets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependsOnTargets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;taskItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TaskItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;WalterlvTargets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么转换成内联版本下面这样。为了方便验证，我直接把完整的 csproj 文件贴出来了。如果你希望在你的项目中去使用，可以只复制 &lt;code class=&quot;highlighter-rouge&quot;&gt;UsingTask&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 两个部分。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UsingTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvGetAllTargets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskFactory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CodeTaskFactory&quot;&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;AssemblyFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ParameterGroup&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 内联 C# 代码的输入参数（Task 的输入属性），相当于 public string ProjectFile { get; set; } --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ProjectFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ParameterType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.String&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Required=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 内联 C# 代码的输出参数（Task 的输入属性），相当于 public ITaskItem[] WalterlvTargets { get; set; } --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvTargets&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ParameterType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Framework.ITaskItem[]&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Output=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ParameterGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Task&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 引用程序集。 --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Xml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Framework&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 编写 C# 代码所用到的 using。 --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Using&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Namespace=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Evaluation&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Using&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Namespace=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Execution&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Using&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Namespace=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Utilities&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Using&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Namespace=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Framework&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 开始插入 C# 代码。 --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Code&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fragment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;![CDATA[
            var project = new Project(ProjectFile);

            var taskItems = new List&amp;lt;ITaskItem&amp;gt;&lt;/span&gt;(project.Targets.Count);
            foreach (KeyValuePair&lt;span class=&quot;nt&quot;&gt;&amp;lt;string&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;ProjectTargetInstance&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; pair in project.Targets)
            {
                var target = pair.Value;
                var metadata = new Dictionary&lt;span class=&quot;nt&quot;&gt;&amp;lt;string&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                {
                    { &quot;Condition&quot;, target.Condition },
                    { &quot;Inputs&quot;, target.Inputs },
                    { &quot;Outputs&quot;, target.Outputs },
                    { &quot;DependsOnTargets&quot;, target.DependsOnTargets }
                };
                taskItems.Add(new TaskItem(pair.Key, metadata));
            }

            WalterlvTargets = taskItems.ToArray();
        ]]&amp;gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Task&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UsingTask&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvOutputAllTargets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 执行刚刚写的内联 Task，然后获取它的输出参数 WalterlvTargets 并填充到 TargetItems 集合中。 --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvGetAllTargets&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ProjectFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildProjectFile)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TargetItems&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvTargets&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvGetAllTargets&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 用一个 Message 输出刚刚生成的 TargetItems 集合中每一项的 Identity 属性（集合中每一项都会输出。） --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;输出的 Target：%(TargetItems.Identity)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt; 命令进行编译，我们将看到所有 Target 的输出：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-01-15-21-18.png&quot; alt=&quot;输出的所有 Target&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;WalterlvOutputAllTargets:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OutputAll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForUnsupportedTargetFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CollectTargetFrameworkForTelemetry&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForUnsupportedNETCoreVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForUnsupportedNETStandardVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForUnsupportedAppHostUsage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForMismatchingPlatform&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForNETCoreSdkIsPreview&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AdjustDefaultPlatformTargetForNetFrameworkExeWithNoNativeCopyLocalItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CreateManifestResourceNames&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveCodeAnalysisRuleSet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlPreCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ShimReferencePathsWhenCommonTargetsDoesNotUnderstandReferenceAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_BeforeVBCSCoreCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;InitializeSourceRootMappedPaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_InitializeSourceRootMappedPathsFromSourceControl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SetPathMapFromSourceRoots&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolvePackageDependenciesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectSDKReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectResolvedSDKReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectPackageReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckCompileDesignTimePrerequisite&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectAnalyzersDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectResolvedCompilationReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectUpToDateCheckInputDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectUpToDateCheckOutputDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectUpToDateCheckBuiltDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CompileDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_FixVCLibs120References&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_AddVCLibs140UniversalCrtDebugReference&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;InitializeSourceControlInformation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForInvalidConfigurationAndPlatform&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Rebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeRebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterRebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildGenerateSources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildGenerateSourcesTraverse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildCompileTraverse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildLink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildLinkTraverse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CopyRunEnvironmentFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuildOnlySettings&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareForBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetFrameworkPaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetReferenceAssemblyPaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworkMoniker&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworkMonikerDisplayName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworkDirectories&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AssignLinkMetadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PreBuildEvent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UnmanagedUnregistration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworkVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeResolveReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterResolveReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AssignProjectConfiguration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SplitProjectReferencesByFileExistence&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetProjectReferenceTargetFrameworkProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetFrameworkProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareProjectReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveProjectReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveProjectReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ExpandSDKReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetTargetPathWithTargetPlatformMoniker&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetNativeManifest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveNativeReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveAssemblyReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FindReferenceAssembliesForReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateBindingRedirects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateBindingRedirectsUpdateAppConfig&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetInstalledSDKLocations&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveSDKReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveSDKReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FindInvalidProjectReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetReferenceTargetPlatformMonikers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ExpandSDKReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ExportWindowsMDFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveAssemblyReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DesignTimeResolveAssemblyReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveComReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveComReferencesDesignTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareResources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareResourceNames&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AssignTargetPaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetItemTargetPaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SplitResourcesByCulture&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CreateCustomManifestResourceNames&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeResGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterResGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreResGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CompileLicxFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveKeySource&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateCompileInputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateTargetFrameworkMonikerAttribute&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateAdditionalSources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_TimeStampBeforeCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateCompileDependencyCache&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_TimeStampAfterCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeNonExistentFileProperty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateSerializationAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CreateSatelliteAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateSatelliteAssemblyInputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateSatelliteAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeIntermediateSatelliteAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetWin32ManifestProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SetExternalWin32ManifestProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SetEmbeddedWin32ManifestProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateResolvedDeploymentManifestEntryPoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateManifests&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateApplicationManifest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentComputeNativeManifestInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentComputeClickOnceManifestInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentGenerateTrustInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateDeploymentManifest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareForRun&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CopyFilesToOutputDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyFilesMarkedCopyLocal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopySourceItemsToOutputDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetCopyToOutputDirectoryItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetCopyToPublishDirectoryItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyOutOfDateSourceItemsToOutputDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyOutOfDateSourceItemsToOutputDirectoryAlways&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyAppConfigFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyManifestFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForCompileOutputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SGenCheckForOutputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UnmanagedRegistration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IncrementalClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CleanGetCurrentAndPriorFileWrites&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Clean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforeClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanReferencedProjects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CleanRecordFileWrites&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanPublishFolder&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PostBuildEvent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Publish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentUnpublishable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetGenerateManifests&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PublishOnly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BeforePublish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterPublish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PublishBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyFilesToPublishFolder&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentGenerateBootstrapper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DeploymentSignClickOnceDeployment&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AllProjectOutputGroups&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuiltProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DebugSymbolsProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DocumentationProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SatelliteDllsProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SourceFilesProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetCompile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ContentFilesProjectOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SGenFilesOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetResolvedSDKReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CollectReferencedNuGetPackages&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PriFilesOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SDKRedistOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AllProjectOutputGroupsDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BuiltProjectOutputGroupDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DebugSymbolsProjectOutputGroupDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SatelliteDllsProjectOutputGroupDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DocumentationProjectOutputGroupDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SGenFilesOutputGroupDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReferenceCopyLocalPathsOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetCABuildNativeEnvironmentVariables&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunCodeAnalysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunNativeCodeAnalysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunSelectedFileNativeCodeAnalysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunMergeNativeCodeAnalysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ImplicitlyExpandDesignTimeFacades&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetWinFXPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DesignTimeMarkupCompilation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareResourcesForSatelliteAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_AfterCompileWinFXInternal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterCompileWinFX&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterMarkupCompilePass1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AfterMarkupCompilePass2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MarkupCompilePass1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MarkupCompilePass2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CompileTemporaryAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MarkupCompilePass2ForMainAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateTemporaryTargetAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanupTemporaryTargetAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AddIntermediateAssemblyToReferenceList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SatelliteOnlyMarkupCompilePass2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;HostInBrowserValidation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SplashScreenValidation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResignApplicationManifest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SignDeploymentManifest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FileClassification&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MainResourcesGeneration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SatelliteResourceGeneration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateResourceWithCultureItem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CheckUid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UpdateUid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RemoveUid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MergeLocalizationDirectives&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AssignWinFXEmbeddedResource&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityDeploy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityDeploySplit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityDeployNonEmbeddedResources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityDeployEmbeddedResources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EntityDeploySetLogicalNames&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DesignTimeXamlMarkupCompilation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;InProcessXamlMarkupCompilePass1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanInProcessXamlGeneratedFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompileReadGeneratedFileList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompilePass1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompileAddFilesGenerated&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompileReadPass2Flag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlTemporaryAssemblyGeneration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CompileTemporaryAssembly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompilePass2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XamlMarkupCompileAddExtensionFilesGenerated&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetCopyToOutputDirectoryXamlAppDefs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ExpressionBuildExtension&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ValidationExtension&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateCompiledExpressionsTempFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AddDeferredValidationErrorsFileToFileWrites&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReportValidationBuildExtensionErrors&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DeferredValidation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveTestReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanAppxPackage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetPackagingOutputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Restore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateRestoreGraphFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_LoadRestoreGraphEntryPoints&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_FilterRestoreGraphProjectInputItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreGraph&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreGraphProjectEntry&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreSpecs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateDotnetCliToolReferenceSpecs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetProjectJsonPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreProjectStyle&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EnableIntermediateOutputPathMismatchWarning&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreTargetFrameworksOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreTargetFrameworksAsItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSettings&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSettingsCurrentProject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSettingsAllFrameworks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSettingsPerFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectSpec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateProjectRestoreGraph&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateProjectRestoreGraphAllFrameworks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateProjectRestoreGraphCurrentProject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateProjectRestoreGraphPerFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectPathItemsCurrentProject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectPathItemsPerFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectPathItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectPathItemsAllFrameworks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GenerateRestoreProjectPathWalk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetAllRestoreProjectPathItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSettingsOverrides&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestorePackagesPathOverride&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreSourcesOverride&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetRestoreFallbackFoldersOverride&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_IsProjectRestoreSupported&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DesktopBridgeCopyLocalOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DesktopBridgeComFilesOutputGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetDeployableContentReferenceOutputs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DockerResolveAppType&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DockerUpdateComposeVsGeneratedFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DockerResolveTargetFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DockerComposeBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DockerPackageService&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ImplicitlyExpandNETStandardFacades&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_RemoveZipFileSuggestedRedirect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetARM64AppxPackageInputsForInboxNetNative&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CleanMdbFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PreXsdCodeGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XsdCodeGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;XsdResolveReferencePath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CleanXsdCodeGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SetTargetFrameworkMonikerAttribute&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolvePackageDependenciesForBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunResolvePackageDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolvePackageAssets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FilterSatelliteResources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunProduceContentAssets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReportAssetsLogMessages&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveLockFileReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IncludeTransitiveProjectReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveLockFileAnalyzers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeLockFileCopyLocal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveLockFileCopyLocalProjectDeps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CheckForImplicitPackageReferenceOverrides&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CheckForDuplicateItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateBuildDependencyFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateBuildRuntimeConfigurationFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AddRuntimeConfigFileToBuiltProjectOutputGroupOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SdkBeforeClean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SdkBeforeRebuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeNETCoreBuildOutputFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeReferenceAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreGenerateSatelliteAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetAssemblyInfoFromTemplateFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_DefaultMicrosoftNETPlatformLibrary&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetAllRuntimeIdentifiers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateAssemblyInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AddSourceRevisionToInformationalVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetAssemblyAttributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CreateGeneratedAssemblyInfoInputsCacheFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CoreGenerateAssemblyInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetAssemblyVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComposeStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoreWorkerMain&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoreWorkerMapper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoreResolver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoreWorkerPerformWork&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoreFinalizer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyResolvedOptimizedFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareForComposeStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepforRestoreForComposeStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RestoreForComposeStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeAndCopyFilesToStoreDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CopyFilesToStoreDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyResolvedUnOptimizedFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeResolvedFilesToStoreTypes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SplitResolvedFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetResolvedFilesToStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeFilesToStore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepRestoreForStoreProjects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepOptimizer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_RunOptimizer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunCrossGen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_InitializeBasicProps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetCrossgenProps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_SetupStageForCrossgen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_RestoreCrossgen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CheckForObsoleteDotNetCliToolReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_PublishBuildAlternative&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_PublishNoBuildAlternative&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_PreventProjectReferencesFromBuilding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PrepareForPublish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeAndCopyFilesToPublishDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CopyFilesToPublishDirectory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyResolvedFilesToPublishPreserveNewest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyResolvedFilesToPublishAlways&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeResolvedFilesToPublishTypes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeFilesToPublish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeNetPublishAssets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RunResolvePublishAssemblies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FilterPublishSatelliteResources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeCopyToPublishDirectoryItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DefaultCopyToPublishDirectoryMetadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GeneratePublishDependencyFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ComputeExcludeFromPublishPackageReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_ParseTargetManifestFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GeneratePublishRuntimeConfigurationFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DeployAppHost&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PackTool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateToolsSettingsFileFromBuildProperty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResolveApphostAsset&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeDependencyFileCompilerOptions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ComputeRefAssembliesToPublish&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CopyReferenceOnlyAssembliesForBuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_HandlePackageFileConflicts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_HandlePublishFileConflicts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetOutputItemsFromPack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetTargetFrameworksOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_PackAsBuildAfterTarget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CleanPackageFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_CalculateInputsOutputsForPack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Pack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_IntermediatePack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GenerateNuspec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_InitializeNuspecRepositoryInformationProperties&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_LoadPackInputItems&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetProjectReferenceVersions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetProjectVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_WalkEachTargetPerFramework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetFrameworksWithSuppressedDependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetFrameworkAssemblyReferences&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetBuildOutputFilesWithTfm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetTfmSpecificContentForPackage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetDebugSymbolsWithTfm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_AddPriFileToPackBuildOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;输出的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;：&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_GetPackageFiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2781693/6233938&quot;&gt;msbuild - Is there a way to list all the build targets available in a build file? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 01 Mar 2019 07:35:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-a-msbuild-inline-task-for-getting-all-targets.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-a-msbuild-inline-task-for-getting-all-targets.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何在 csproj 中用 C# 代码写一个内联的编译任务 Task</title>
        <description>&lt;p&gt;我之前写过一些改变 MSBuild 编译过程的一些博客，包括利用 Microsoft.NET.Sdk 中各种自带的 Task 来执行各种各样的编译任务。更复杂的任务难以直接利用自带的 Task 实现，需要自己写 Task。&lt;/p&gt;

&lt;p&gt;本文介绍非常简单的 Task 的编写方式 —— 在 csproj 文件中写内联的 Task。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;前置知识&quot;&gt;前置知识&lt;/h2&gt;

&lt;p&gt;在阅读本文之前，你至少需要懂得：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;csproj 文件的结构以及编译过程&lt;/li&gt;
  &lt;li&gt;Target 是什么，Task 是什么&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以如果你不懂或者理不清，则请先阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 Task 的理解，我有一些介绍自带 Task 的博客以及如何编写 Task 的教程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target（附各种自带的 Task） - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;编写内联的编译任务task&quot;&gt;编写内联的编译任务（Task）&lt;/h2&gt;

&lt;p&gt;如果你阅读了前面的博客，那么大致知道如何写一个在编译期间执行的 Task。不过，默认你需要编写一个额外的项目来写 Task，然后将这个项目生成 dll 供编译过程通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;UsingTask&lt;/code&gt; 来使用。然而如果 Task 足够简单，那么依然需要那么复杂的过程显然开发成本过高。&lt;/p&gt;

&lt;p&gt;于是现在可以编写内联的 Task：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;内联任务的支持需要用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Build.Tasks.v4.0.dll&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;我们用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;![CDATA[ ]]&amp;gt;&lt;/code&gt; 来内嵌 C# 代码；&lt;/li&gt;
  &lt;li&gt;除了用 &lt;code class=&quot;highlighter-rouge&quot;&gt;UsingTask&lt;/code&gt; 编写内联的 Task 外，我们需要额外编写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 来验证我们的内联 Task 能正常工作。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面是一个最简单的内联编译任务：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UsingTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTask&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskFactory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CodeTaskFactory&quot;&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;AssemblyFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Task&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Code&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fragment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;cp&quot;&gt;&amp;lt;![CDATA[
        Console.WriteLine(&quot;Hello Walterlv!&quot;);
                ]]&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Task&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UsingTask&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了能够测试，我把完整的 csproj 文件贴出来：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UsingTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTask&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskFactory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CodeTaskFactory&quot;&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;AssemblyFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Task&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Code&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fragment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Language=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;cp&quot;&gt;&amp;lt;![CDATA[
        Console.WriteLine(&quot;Hello Walterlv!&quot;);
                ]]&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Task&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UsingTask&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvDemoTask&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;目前内联编译仅适用于 MSBuild，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 尚不支持。现在在项目目录输入命令进行编译，可以在输出窗口看到我们内联编译中的输出内容：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-03-01-15-09-19.png&quot; alt=&quot;输出内容&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;编写更复杂的内联编译任务&quot;&gt;编写更复杂的内联编译任务&lt;/h2&gt;

&lt;p&gt;阅读我的另一篇博客了解如何编写一个更复杂的内联编译任务：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-a-msbuild-inline-task-for-getting-all-targets&quot;&gt;编写 MSBuild 内联编译任务（Task）用于获取当前编译环境下的所有编译目标（Target） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 01 Mar 2019 07:12:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-msbuild-inline-task.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-msbuild-inline-task.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 MSBuild 编译项目时阻止输出所有的警告信息</title>
        <description>&lt;p&gt;大型旧项目可能存在大量的 Warning，在编译之后 Visual Studio 会给出大量的警告。Visual Studio 中可以直接点掉警告，然而如果是通过命令行 msbuild 编译的，那如何不要让警告输出呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在使用 msbuild 命令编译项目的时候，如果存在大量的警告，输出量会非常多。如果我们使用 msbuild 命令编译来定位项目的编译错误，那么这些警告将会导致我们准确查找错误的效率明显降低。&lt;/p&gt;

&lt;p&gt;当然，&lt;strong&gt;这种问题的首选解决方案是 —— 真的修复掉这些警告&lt;/strong&gt;！！！&lt;/p&gt;

&lt;p&gt;那么可以用什么方式临时关闭 msbuild 命令编译时的警告呢？可以输入如下命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/p:WarningLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样在调试编译问题的时候，因警告而造成的大量输出信息就会少很多。&lt;/p&gt;

&lt;p&gt;不过需要注意的是，这种方式不会关闭所有的警告，实际上这关闭的是 csc 命令的警告（&lt;code class=&quot;highlighter-rouge&quot;&gt;CS&lt;/code&gt; 开头）。关于 csc 命令的警告可以参见：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/warn-compiler-option&quot;&gt;-warn (C# Compiler Options) - Microsoft Docs&lt;/a&gt;。于是，如果项目中存在 msbuild 的警告（&lt;code class=&quot;highlighter-rouge&quot;&gt;MSB&lt;/code&gt; 开头），此方法依然还会输出，只不过如果是为了调试编译问题，那么依然会方便很多，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSB&lt;/code&gt; 开头的警告会少非常多。&lt;/p&gt;

&lt;p&gt;关于警告等级：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;	关闭所有的警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;	仅显示严重警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;2&lt;/code&gt;	显示 1 级的警告以及某些不太严重的警告，例如有关隐藏类成员的警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;3&lt;/code&gt;	显示级别 2 警告以及某些不太严重的警告，例如关于始终评估为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 的表达式的警告。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;4&lt;/code&gt;   &lt;em&gt;默认值&lt;/em&gt; 显示所有 3 级警告和普通信息警告。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/1023858/6233938&quot;&gt;command line - How to suppress specific MSBuild warning - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/2050826/6233938&quot;&gt;command line - How to suppress all warnings using MSBuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/25565610/6233938&quot;&gt;visual studio 2013 - How to have MSBuild quiet output but with error/warning summary - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/warn-compiler-option&quot;&gt;-warn (C# Compiler Options) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/96b3ea2e-92ed-4483-bbfe-a4dda3231eb9/suppress-msb4126&quot;&gt;Suppress MSB4126&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 09:35:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/suppress-all-warnings-for-msbuild.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/suppress-all-warnings-for-msbuild.html</guid>
        
        
        <category>msbuild</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>帮助官方 NuGet 解掉 Bug，制作绝对不会传递依赖的 NuGet 包</title>
        <description>&lt;p&gt;如果你希望做一个 NuGet 工具包，那么这个包一定不能作为依赖传递给下一个包。典型的例子，做一个生成版本号的工具 NuGet 包，或者做一个代码分析器。&lt;/p&gt;

&lt;p&gt;本文将解决 NuGet 的几个坑，真正做到绝对没有的依赖传递。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;我们遇到了什么问题&quot;&gt;我们遇到了什么问题&lt;/h2&gt;

&lt;p&gt;如果你使用了 GitVersion 这款 NuGet 包来自动修改你的版本号，那么你可能会遇到这个问题。&lt;a href=&quot;https://github.com/GitTools/GitVersion&quot;&gt;GitTools/GitVersion: Easy Semantic Versioning (http://semver.org) for projects using Git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;假想我们希望开发一个 NuGet 包 Walterlv.PackageDemo.A。另一位小伙伴想要使用我 A 包的功能做一个 Walterlv.PackageDemo.B 包。于是其他小伙伴可以安装 B 包去做自己的项目 C。&lt;/p&gt;

&lt;p&gt;那么，除非我在 B 包安装完之后，明确在 B 的 csproj 文件中写以下代码，否则 B 包发布出去后，安装 B 包的项目 C 就会同时安装上 A 包。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.PackageDemo.A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;显然，由于 A 是个工具包，只是为了给安装了 A 的 B 包提供版本号或其他编译期功能的。C 不需要这样的功能！&lt;/p&gt;

&lt;p&gt;然而我们希望做出来的 A 包具备这样的特点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;小伙伴给 B 安装 A 包的时候，不用额外为 A 包写配置依赖的代码；&lt;/li&gt;
  &lt;li&gt;小伙伴为 C 安装 B 的时候，不会出现 A 乱入的情况。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你依然对这样的问题存有疑惑，可以阅读以下文章，这是切实的例子。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;官方提供的解决方案&quot;&gt;官方提供的解决方案&lt;/h2&gt;

&lt;p&gt;&lt;del&gt;官方在非常早期的 2.7 版本就提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;developmentDependency&lt;/code&gt; 属性，可以在 nuspec 文件中写。但实际上这个属性在后面版本的 NuGet 开发中就丢掉了。不生效。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;官方提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsTool&lt;/code&gt; 属性可以使用，但这依然不能阻止 B 安装了 A 包之后，C 包被迫安装 A 包的问题。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;更新：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;官方的 NuGet 在更新到 4.9 及以上之后，此属性再次生效。添加方法是在 csproj 文件中添加一行属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;添加后，如果开发者安装了此 NuGet 包，将自动生成以下代码，于是依赖就不会传递。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;PackageReference Include=&quot;dotnetCampus.SourceYard&quot; Version=&quot;0.1.7213-alpha&quot;&amp;gt;
    &amp;lt;PrivateAssets&amp;gt;all&amp;lt;/PrivateAssets&amp;gt;
    &amp;lt;IncludeAssets&amp;gt;runtime; build; native; contentfiles; analyzers; buildtransitive&amp;lt;/IncludeAssets&amp;gt;
&amp;lt;/PackageReference&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于官方解决掉这个 Bug，所以你只需要更新到 NuGet 4.9 以上的版本即可。后面的博客内容无需阅读。&lt;/p&gt;

&lt;p&gt;至于如何查看自己的 NuGet 版本号，请参见：&lt;a href=&quot;https://blog.lindexi.com/post/%E8%8E%B7%E5%8F%96-nuget-%E7%89%88%E6%9C%AC%E5%8F%B7&quot;&gt;获取 Nuget 版本号 - 林德熙&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;我试图寻找的解决方案&quot;&gt;&lt;del&gt;我试图寻找的解决方案&lt;/del&gt;&lt;/h2&gt;

&lt;h3 id=&quot;为-a-项目添加去除依赖的代码&quot;&gt;&lt;del&gt;为 A 项目添加去除依赖的代码&lt;/del&gt;&lt;/h3&gt;

&lt;p&gt;&lt;del&gt;我们创建一个项目 Walterlv.PackageDemo.A 模拟前面提到的包 A，创建一个项目 Walterlv.PackageDemo.B 模拟前面提到的包 B，创建一个项目 Walterlv.ProjectDemo.C 模拟前面的项目 C。注意，实际场景中，这三个项目通常在不同的仓库中，由不同的开发者开发。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-30-19-52-46.png&quot; alt=&quot;创建项目 A、B、C&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;不过，为了方便起见，我打算直接在一个解决方案中模拟这样的效果：&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-20-40-37.png&quot; alt=&quot;在一个解决方案中模拟&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;我在 A 中试图创建一个 build\Walterlv.PackageDemo.A.props 或 build\Walterlv.PackageDemo.A.targets 文件，并在里面写一些阻止 A 被依赖的代码。&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.PackageDemo.A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;del&gt;为了通用一点，我取名为 Package.targets 文件，并在 A 项目编译的时候改名为 Walterlv.PackageDemo.A.targets。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-21-05-18.png&quot; alt=&quot;Package.targets 文件&quot; /&gt;&lt;br /&gt;
▲ 项目的结构&lt;/p&gt;

&lt;p&gt;&lt;del&gt;以下是 A 项目的 csproj 文件，包含将 Package.targets 在打包 NuGet 包时改名的部分。&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\Package.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;在-b-项目中进行测试&quot;&gt;&lt;del&gt;在 B 项目中进行测试&lt;/del&gt;&lt;/h3&gt;

&lt;p&gt;&lt;del&gt;本地调试当然用不着推送到 &lt;a href=&quot;https://nuget.org&quot;&gt;https://nuget.org&lt;/a&gt;。我们本地新建一个源，专门用于调试。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;在 “工具 -&amp;gt; 选项 -&amp;gt; NuGet 包管理器” 中，我们可以设置 NuGet 源：&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-21-02-07.png&quot; alt=&quot;添加调试用的 NuGet 源&quot; /&gt;&lt;br /&gt;
▲ 添加调试用的 NuGet 源&lt;/p&gt;

&lt;p&gt;&lt;del&gt;我们把刚刚 A 项目的输出目录填进去添加一个新的源。于是我们就能在 B 项目中安装 A 包了。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;于是 B 项目的 csproj 文件全文内容如下：&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.PackageDemo.A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;del&gt;当以上 A 和 B 项目被 Visual Studio 编译的时候，一切符合预期；就像下图这样，B 项目中没有声明对 A 的依赖：&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-21-15-42.png&quot; alt=&quot;B 项目的 NuGet 包&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;令人遗憾的结果&quot;&gt;&lt;del&gt;令人遗憾的结果&lt;/del&gt;&lt;/h3&gt;

&lt;p&gt;&lt;del&gt;然而使用命令行编译的时候，就不按照预期工作了；如下图这样，B 项目中出现了对 A 的依赖。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-16-16-37-56.png&quot; alt=&quot;B 项目的 NuGet 包，有依赖&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;命令行编译时使用这些命令效果都是一样的不管用。&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;msbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;del&gt;不过，令人难以置信的时，如果此时 Visual Studio 打开了此项目，命令行编译却能符合预期。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;另外，我还尝试将 Package.targets 中的所有内容放到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt; 里面以获得延迟到编译期执行的效果，但结论依然与上面一致，即仅能在 Visual Studio 中正常工作。&lt;/del&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ForceWalterlvDemoPrivateAssets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectPackageReferences&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.PackageDemo.A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PrivateAssets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 08:34:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/prevent-nuget-package-been-depended.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/prevent-nuget-package-been-depended.html</guid>
        
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>电脑总是意外从睡眠状态唤醒，可以找出原因然后解决</title>
        <description>&lt;p&gt;在昏暗的夜晚，一个人躺在房间的床上，静静的思考着什么。突然间电脑屏幕亮了！什么鬼！到底是谁唤醒了我的电脑！！！&lt;/p&gt;

&lt;p&gt;本文将介绍如何寻找唤醒电脑的真凶。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;调查是谁唤醒了电脑&quot;&gt;调查是谁唤醒了电脑&lt;/h2&gt;

&lt;p&gt;使用命令查看上一次是谁唤醒了电脑。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;powercfg -lastwake
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-18-09-16-28.png&quot; alt=&quot;last wake&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中可知上一次唤醒我计算机的是 &lt;a href=&quot;https://www.intel.cn/content/www/cn/zh/products/network-io/ethernet/controllers/connection-i219-v.html&quot;&gt;英特尔® 以太网连接 I219-V 82186&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;查看还有谁可以唤醒电脑&quot;&gt;查看还有谁可以唤醒电脑&lt;/h2&gt;

&lt;p&gt;使用命令查看所有可以唤醒电脑的设备。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;powercfg -devicequery wake_armed
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-18-09-16-46.png&quot; alt=&quot;wake armed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;发现能唤醒我电脑的设备是键盘鼠标以及刚刚的以太网。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-18-09-17-08.png&quot; alt=&quot;wake timers&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;查看下一次计划的唤醒&quot;&gt;查看下一次计划的唤醒&lt;/h2&gt;

&lt;p&gt;使用命令可以查看下一次计划的唤醒。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cmd&quot;&gt;powercfg -waketimers
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当然这只能查到计划的唤醒，类似鼠标键盘还有以太网这种根据硬件状态触发的唤醒是看不到的。&lt;/p&gt;

&lt;h2 id=&quot;修复意外的唤醒&quot;&gt;修复意外的唤醒&lt;/h2&gt;

&lt;p&gt;由于我不知道到底是谁通过以太网唤醒了我的电脑，所以我直接关掉以太网的唤醒即可。&lt;/p&gt;

&lt;p&gt;前往设备管理器，找到刚刚发现的硬件设备，查看属性。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-14-03-21.png&quot; alt=&quot;设备管理器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后我关闭了此设备唤醒电脑的设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-14-03-52.png&quot; alt=&quot;关闭唤醒电脑&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ghacks.net/2013/12/31/find-pc-wakes-stop/&quot;&gt;How to find out why your PC wakes up, and how to stop it - gHacks Tech News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 06:09:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/find-out-the-reason-that-wakes-the-pc-up.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/find-out-the-reason-that-wakes-the-pc-up.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>将友盟（cnzz）站点的访问数据报告分享给其他人</title>
        <description>&lt;p&gt;友盟（cnzz）可以帮助我们分析站点的访问数据。不过如果有更多的决策者，则需要更多人可以访问到友盟的数据。&lt;/p&gt;

&lt;p&gt;本文介绍两种将自己站点的访问数据报告分享给其他人的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;首先，你必须有一个友盟产品的账号，如果还没有，去 注册吧！&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用查看密码分享&quot;&gt;使用查看密码分享&lt;/h2&gt;

&lt;h3 id=&quot;设置&quot;&gt;设置&lt;/h3&gt;

&lt;p&gt;进入 &lt;a href=&quot;https://web.umeng.com/main.php?c=site&amp;amp;a=show&quot;&gt;站点列表&lt;/a&gt; 页面。&lt;/p&gt;

&lt;p&gt;点击网站右侧的“设置”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-06-59.png&quot; alt=&quot;点击设置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后进入查看密码标签，开启查看密码服务，然后输入查看密码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-31-17.png&quot; alt=&quot;输入查看密码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这种方式的好处在于非常简单，你只需要告诉他人你的查看密码，其他人随时可以点开你网站的数据统计链接查看站点的访问数据。&lt;/p&gt;

&lt;h3 id=&quot;web-端查看&quot;&gt;Web 端查看&lt;/h3&gt;

&lt;p&gt;在 Web 端点开站点底部的访问数据即可进入数据报表页面。输入查看密码即可查看。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-34-18.png&quot; alt=&quot;进入 Web 端的访问数据页面&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在移动端查看&quot;&gt;在移动端查看&lt;/h3&gt;

&lt;p&gt;cnzz 移动端下载安装后首页有四个按钮。点击“查看密码”后输入站点 Id 或扫码，然后输入密码即可查看数据。&lt;/p&gt;

&lt;p&gt;不过移动端的 cnzz 做得很烂，如果登录过自己的站点查看数据，那么使用密码查看必崩，而且至今未修复。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-10-02-12.png&quot; alt=&quot;移动端&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用授权账号分享&quot;&gt;使用授权账号分享&lt;/h2&gt;

&lt;p&gt;本来使用密码查看是非常方便的，但是为了解决崩溃问题，还是需要使用授权账号来查看数据。&lt;/p&gt;

&lt;h3 id=&quot;设置-1&quot;&gt;设置&lt;/h3&gt;

&lt;p&gt;进入 &lt;a href=&quot;https://web.umeng.com/main.php?c=site&amp;amp;a=show&quot;&gt;站点列表&lt;/a&gt; 页面。&lt;/p&gt;

&lt;p&gt;点击网站右上角的“授权”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-36-12.png&quot; alt=&quot;点击授权&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果还没有授权给其他人，则可以点击“添加授权账号”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-37-15.png&quot; alt=&quot;添加授权账号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后输入对方的友盟账号和邮箱，添加对方的权限。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-09-43-05.png&quot; alt=&quot;设置邀请信息&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在-web-端查看&quot;&gt;在 Web 端查看&lt;/h3&gt;

&lt;p&gt;进入 &lt;a href=&quot;https://web.umeng.com/main.php?c=site&amp;amp;a=show&quot;&gt;站点列表&lt;/a&gt; 页面，可以在被授权站点看到授权查看的站点数据了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-13-48-19.png&quot; alt=&quot;查看被授权网站的访问数据&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;在移动端查看-1&quot;&gt;在移动端查看&lt;/h3&gt;

&lt;p&gt;cnzz 移动端下载安装后首页有四个按钮。点击“网站统计”后可以看到自己的站点和被授权的站点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-10-02-12.png&quot; alt=&quot;移动端&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 05:48:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/share-cnzz-report-to-others.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/share-cnzz-report-to-others.html</guid>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>全局或为单独的项目添加自定义的 NuGet 源</title>
        <description>&lt;p&gt;本文介绍如何添加自定义的 NuGet 源。包括全局所有项目生效的 NuGet 源和仅在某些特定项目中生效的 NuGet 源。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;你可以前往 &lt;a href=&quot;/post/public-nuget-sources&quot;&gt;我收集的各种公有 NuGet 源&lt;/a&gt; 以发现更多的 NuGet 源，然后使用本文的方法添加到你自己的配置中。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用命令行添加&quot;&gt;使用命令行添加&lt;/h2&gt;

&lt;p&gt;在使用命令行之前，你需要先在 &lt;a href=&quot;https://www.nuget.org/downloads&quot;&gt;https://www.nuget.org/downloads&lt;/a&gt; 下载最新的 nuget.exe 然后加入到环境变量中。&lt;/p&gt;

&lt;p&gt;现在，我们使用命令行来添加一个包含各种日构建版本的 NuGet 源 MyGet：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MyGet&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你添加的只是一个镜像源（比如华为云 huaweicloud），那么其功能和官方源是重合的，可以禁用掉官方源：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Disable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nuget.org&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nuget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;huaweicloud&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://mirrors.huaweicloud.com/repository/nuget/v3/index.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在-visual-studio-中添加&quot;&gt;在 Visual Studio 中添加&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 中打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;工具&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;NuGet 包管理器&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;包源&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-27-11-58-37.png&quot; alt=&quot;管理包源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在界面上添加、删除、启用和禁用 NuGet 源。&lt;/p&gt;

&lt;p&gt;值得注意的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 Visual Studio 中是不能禁用掉官方源 &lt;code class=&quot;highlighter-rouge&quot;&gt;nuget.org&lt;/code&gt; 的，无论你如何取消勾选，实际都不会生效。
    &lt;ul&gt;
      &lt;li&gt;如果要取消，你需要用命令行或者手工编辑配置文件。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;你可以添加一个本地路径作为本地 NuGet 源，而那个路径只要存在 *.nupkg 文件就够了。
    &lt;ul&gt;
      &lt;li&gt;对于 .NET Core 项目，勾选编译后生成 NuGet 包则会在输出路径生成这样的文件，于是你可以本地调试。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;直接修改配置文件&quot;&gt;直接修改配置文件&lt;/h2&gt;

&lt;p&gt;NuGet 的全局配置文件在 &lt;code class=&quot;highlighter-rouge&quot;&gt;%AppData\NuGet\NuGet.config&lt;/code&gt;，例如：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Users\lvyi\AppData\Roaming\NuGet\NuGet.Config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;直接修改这个文件的效果跟使用命令行和 Visual Studio 的界面配置是等价的。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;huaweicloud&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://repo.huaweicloud.com/repository/nuget/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nuget.org&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://api.nuget.org/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocolVersion=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Debug&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C:\Users\lvyi\Debug\Walterlv.NuGet&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft Visual Studio Offline Packages&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyGet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageSources&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;disabledPackageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft Visual Studio Offline Packages&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Debug&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nuget.org&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/disabledPackageSources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;为单独的项目添加自定义的-nuget-源&quot;&gt;为单独的项目添加自定义的 NuGet 源&lt;/h2&gt;

&lt;p&gt;NuGet.config 文件是有优先级的。nuget.exe 会先把全局配置加载进来；然后从当前目录中寻找 NuGet.config 文件，如果没找到就去上一级目录找，一直找到驱动器的根目录；找到后添加到已经加载好的全局配置中成为一个合并的配置。&lt;/p&gt;

&lt;p&gt;所以我们只需要在项目的根目录放一个 NuGet.config 文件并填写相比于全局 NuGet.config 新增的配置即可为单独的项目添加 NuGet 配置。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 下一行的 clear 如果取消了注释，那么就会清除掉全局的 NuGet 源，而注释掉可以继承全局 NuGet 源，只是额外添加。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;clear /&amp;gt; --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyGet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageSources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 04:26:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-custom-nuget-source.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-custom-nuget-source.html</guid>
        
        
        <category>nuget</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>使用 Visual Studio 调试多进程的程序</title>
        <description>&lt;p&gt;当你的编写的是一个多进程的程序的时候，调试起来可能会比较困难，因为 Visual Studio 默认只会把你当前设置的启动项目的启动调试。&lt;/p&gt;

&lt;p&gt;本文将介绍几种用 Visual Studio 调试多进程程序的方法，然后给出每种方法的适用条件和优劣。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;visual-studio-多启动项目推荐&quot;&gt;Visual Studio 多启动项目（推荐）&lt;/h2&gt;

&lt;p&gt;在 Visual Studio 的解决方案上点击右键，属性。在公共属性节点中选择启动项目。&lt;/p&gt;

&lt;p&gt;在这里，你可以给多个项目都设置成启动项目，就像下图这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-53-42.png&quot; alt=&quot;设置多启动项目&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，这些项目都必须要是能够启动的才行（不一定是可执行程序）。&lt;/p&gt;

&lt;p&gt;此方案的好处是 Visual Studio 原生支持。但此方案的使用必须满足两个前提：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;要调试的多个进程必须是不同的项目编译出来的；&lt;/li&gt;
  &lt;li&gt;这些项目之间的启动顺序不能有明显的依赖关系（所以你可能需要修改你的代码使得这两个进程之间可以互相唤起）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;microsoft-child-process-debugging-power-tool-插件推荐&quot;&gt;Microsoft Child Process Debugging Power Tool 插件（推荐）&lt;/h2&gt;

&lt;h3 id=&quot;安装和配置插件&quot;&gt;安装和配置插件&lt;/h3&gt;

&lt;p&gt;请先安装 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool&quot;&gt;Microsoft Child Process Debugging Power Tool&lt;/a&gt; 插件。&lt;/p&gt;

&lt;p&gt;安装插件后启动 Visual Studio，可以在 Debug -&amp;gt; Other Debugging Targets 中找到 Child Process Debugging Settings。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-21-48-22.png&quot; alt=&quot;打开 Child Process Debugging Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后你可以按照下图的设置开启此项目的子进程调试：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-21-52-07.png&quot; alt=&quot;设置子进程调试&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置项目启动选项&quot;&gt;配置项目启动选项&lt;/h3&gt;

&lt;p&gt;但是，子进程要能够调试，你还必须开启混合模式调试，开启方法请参见我的另一篇博客：&lt;a href=&quot;/post/visual-studio-enable-native-code-debugging&quot;&gt;在 Visual Studio 新旧不同的 csproj 项目格式中启用混合模式调试程序（开启本机代码调试） - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;现在，你只需要开始调试你的程序，那么你程序中启动的新的子进程都将可以自动加入调试。&lt;/p&gt;

&lt;h3 id=&quot;例子源码和效果&quot;&gt;例子源码和效果&lt;/h3&gt;

&lt;p&gt;现在，我们拿下面这段代码作为例子来尝试子进程的调试。下面的代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 中的代码会运行在子进程中，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 中的代码会运行在主进程中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Debugging&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv child application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv main application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;StartInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;--child&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitForExit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 中都打上断点。正常情况下运行，只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 中的代码可以进断点；而如果以上子进程调试配置正确，那么两边你都可以进入断点（如下图）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-22-51-12.png&quot; alt=&quot;子进程进入了调试断点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;值得注意的是，只要启动了本机代码调试，就不能在程序暂停之后修改代码了（像平时调试纯托管代码那样）。&lt;/p&gt;

&lt;h2 id=&quot;在代码中编写附加调试器&quot;&gt;在代码中编写“附加调试器”&lt;/h2&gt;

&lt;p&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debugger.Launch()&lt;/code&gt; 可以启动一个调试器来调试此进程。于是我们可以在我们被调试的程序中写下如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#if DEBUG
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;仅在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DEBUG&lt;/code&gt; 条件下，如果当前没有附加任何调试器，那么就启动一个新的调试器来调试它。&lt;/p&gt;

&lt;p&gt;当存在以上代码时，运行会弹出一个对话框，用于选择调试器。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-21-08-19-53.png&quot; alt=&quot;选择调试器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里选择的调试器有个不太方便的地方，如果调试器已经在使用，那么就不能选择。对于我们目前的场景，我们的主进程已经在调试了，所以子进程选择调试器的时候不能再选择主进程调试所用的 Visual Studio 了，而只能选择一个新的 Visual Studio；这一点很不方便。&lt;/p&gt;

&lt;p&gt;对于此方法，我的建议是平常不要在团队项目中使用（这会让团队中的其他人不方便）。但是由于代码简单不需要配置，所以临时使用的话还是非常建议的。&lt;/p&gt;

&lt;h2 id=&quot;在代码中调用-visual-studio-的-com-组件-api&quot;&gt;在代码中调用 Visual Studio 的 COM 组件 API&lt;/h2&gt;

&lt;p&gt;编写中……&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;综上，虽然我给出了 4 种不同的方法，但实际上没有任何一种方法能够像我们调试单个原生托管程序那样方便。每一种方法都各有优劣，一般情况下建议你使用我标注了“推荐”的方法；不过也建议针对不同的情况采用不同的方案。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;简单的个人项目，希望快速开始多进程/子进程调试
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;使用附加调试器&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;你有多个项目组成的多进程，并且这些进程恰好可以互相唤起，它们之间的启动顺序不影响父子进程的组成
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;使用 Visual Studio 的多启动项目&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;你只有单个项目组成的多进程，或者多个进程之间依赖于启动顺序来组成父子进程
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;安装插件 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool&quot;&gt;Microsoft Child Process Debugging Power Tool&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/devops/introducing-the-child-process-debugging-power-tool/&quot;&gt;Azure DevOps Blog - Introducing the Child Process Debugging Power Tool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool&quot;&gt;Microsoft Child Process Debugging Power Tool - Visual Studio Marketplace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/337c252e-98b3-4e88-b380-e9a58d88a706/attach-a-process-to-current-visual-studio-debugger-silently-using-command-line-?forum=vsdebug&quot;&gt;attach a process to current visual studio debugger silently using command line ..&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/kirillosenkov/2011/08/10/how-to-get-dte-from-visual-studio-process-id/&quot;&gt;How to get DTE from Visual Studio process ID? – Kirill Osenkov&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/kirillosenkov/2009/03/03/how-to-start-visual-studio-programmatically/&quot;&gt;How to start Visual Studio programmatically – Kirill Osenkov&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/envdte?view=visualstudiosdk-2017&quot;&gt;EnvDTE Namespace - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/19374401/6233938&quot;&gt;c# - Using the EnvDTE assembly - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 21 Feb 2019 07:24:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/debug-multi-process-application-using-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/debug-multi-process-application-using-visual-studio.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>将 svn 仓库迁移到 git 仓库</title>
        <description>&lt;p&gt;我找到了一个很久很久以前编写的项目，然而当时是使用 svn 进行版本管理的。然而现在的版本管理全部是 git，不愿意再装一个 svn 工具来管理这些古老的项目，于是打算将其迁移到 git 中。&lt;/p&gt;

&lt;p&gt;本文介绍如何将古老的 svn 项目迁移到 git。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;找回-svn-仓库的-url&quot;&gt;找回 svn 仓库的 url&lt;/h2&gt;

&lt;p&gt;如果你能记得你 svn 仓库的 url，或者这个仓库是一个纯本地仓库，那么你直接复制这个 url 就好了。&lt;/p&gt;

&lt;p&gt;然而如果这是一个有 svn 远程服务器的仓库，那么你可能依然需要临时安装一下 svn 工具。我们只是为了拿回 url 而已。&lt;/p&gt;

&lt;p&gt;这里我使用当时使用的小乌龟 &lt;a href=&quot;https://tortoisesvn.net/index.zh.html&quot;&gt;TortoiseSVN&lt;/a&gt;。在 svn 仓库空白处右击选择版本库浏览器（Repo-browser），小乌龟会自动定位到当前仓库所在的远程 svn 服务器的对应文件夹。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-10-11-00.png&quot; alt=&quot;版本库浏览器&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们所要做的只有一件事——复制顶部那个 url。&lt;/p&gt;

&lt;p&gt;得到了这个 url 后，像我这种洁癖就卸载 TortoiseSVN 了。&lt;/p&gt;

&lt;h2 id=&quot;将-svn-仓库迁移到-git-仓库&quot;&gt;将 svn 仓库迁移到 git 仓库&lt;/h2&gt;

&lt;h3 id=&quot;命令行&quot;&gt;命令行&lt;/h3&gt;

&lt;p&gt;在一个新的文件夹中，我们输入如下命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git.exe svn clone &lt;span class=&quot;s2&quot;&gt;&quot;https://svn.walterlv.com/LvYi/Timer&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\W&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alterlv.RepoFromSvn&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果那个 svn 目录中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;trunk&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;tags&lt;/code&gt; 结构，那么可以在后面添加相应的参数以便在 clone 完成后保留分支和标签信息。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git.exe svn clone &lt;span class=&quot;s2&quot;&gt;&quot;https://svn.walterlv.com/LvYi/Timer&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\W&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alterlv.RepoFromSvn&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-T&lt;/span&gt; trunk &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; branches &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; tags
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意的是，上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.RepoFromSvn&lt;/code&gt; 文件夹是不允许提前存在的，如果存在将无法迁移成功。&lt;/p&gt;

&lt;h3 id=&quot;tortoisegit&quot;&gt;TortoiseGit&lt;/h3&gt;

&lt;p&gt;这里特地照顾一下从 TortoiseSVN 迁移来继续考虑 &lt;a href=&quot;https://tortoisegit.org/&quot;&gt;TortoiseGit&lt;/a&gt; 的小伙伴。在 TortoiseGit 中的操作是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在某个文件夹中右键（或者 Shift+右键）&lt;/li&gt;
  &lt;li&gt;选择克隆&lt;/li&gt;
  &lt;li&gt;按照下图填写来自 url 的远程服务器 url 和本地文件夹，并打勾“从SVN版本库”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-20-10-19-21.png&quot; alt=&quot;TortoiseGit 上的迁移 SVN 操作&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git&quot;&gt;Git - Migrating to Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9128344/how-to-get-svn-remote-repository-url&quot;&gt;How to get svn remote repository URL? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/articles/perform-migration-from-svn-to-git?view=azure-devops&quot;&gt;Migrate from Subversion (SVN) to Git - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 20 Feb 2019 02:22:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/migrating-svn-to-git.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/migrating-svn-to-git.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET/C# 将一个命令行参数字符串转换为命令行参数数组 args</title>
        <description>&lt;p&gt;我们通常得到的命令行参数是一个字符串数组 &lt;code class=&quot;highlighter-rouge&quot;&gt;string[] args&lt;/code&gt;，以至于很多的命令行解析库也是使用数组作为解析的参数来源。&lt;/p&gt;

&lt;p&gt;然而如我我们得到了一整个命令行字符串呢？这个时候可能我们原有代码中用于解析命令行的库或者其他辅助函数不能用了。那么如何转换成数组呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 Windows 系统中有函数 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw&quot;&gt;CommandLineToArgvW&lt;/a&gt; 可以直接将一个字符串转换为命令行参数数组，我们可以直接使用这个函数。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;LPWSTR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLineToArgvW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;LPCWSTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpCmdLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pNumArgs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此函数在 shell32.dll 中，于是我们可以在 C# 中调用此函数。&lt;/p&gt;

&lt;p&gt;为了方便使用，我将其封装成了一个静态方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommandLineExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertCommandLineToArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLineToArgvW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Win32Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;在转换命令行参数的时候出现了错误。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadIntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStringUni&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FreeHGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;shell32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CommandLineToArgvW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MarshalAs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnmanagedType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LPWStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpCmdLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pNumArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw&quot;&gt;CommandLineToArgvW function - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://intellitect.com/converting-command-line-string-to-args-using-commandlinetoargvw-api/&quot;&gt;Converting Command Line String to Args[] using CommandLineToArgvW() API - IntelliTect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/749653/6233938&quot;&gt;Split string containing command-line parameters into string[] in C# - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Feb 2019 13:49:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/convert-command-line-string-to-args-array.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/convert-command-line-string-to-args-array.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>git 合并策略</title>
        <description>&lt;p&gt;不清楚 git 冲突的表示方法，不了解 git 的合并原理，不知道 git 解冲突的多种策略。即便如此，大多数人依然可以正常使用 git 完成合并、拉取操作，并且解一些冲突。这得益于 git 默认情况下的合并方式可以处理大多数情况下的正常合并。&lt;/p&gt;

&lt;p&gt;然而，你是否遭遇 git 自动合并炸掉的情况？命名提示没有冲突，代码却早已无法编译通过。&lt;/p&gt;

&lt;p&gt;本文将介绍 git 的合并策略，你可能可以更好的使用不同的策略来解决冲突。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;git-合并策略&quot;&gt;git 合并策略&lt;/h2&gt;

&lt;p&gt;典型的使用指定 git 合并策略的命令这么写：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge 要合并进来的分支名 &lt;span class=&quot;nt&quot;&gt;--strategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;合并策略
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge origin/master &lt;span class=&quot;nt&quot;&gt;--strategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;resolve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者使用简写 &lt;code class=&quot;highlighter-rouge&quot;&gt;-s&lt;/code&gt;，例如：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge origin/master &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; resolve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以指定的合并策略有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;resolve&lt;/li&gt;
  &lt;li&gt;recursive&lt;/li&gt;
  &lt;li&gt;octopus&lt;/li&gt;
  &lt;li&gt;ours&lt;/li&gt;
  &lt;li&gt;subtree&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;resolve&quot;&gt;resolve&lt;/h2&gt;

&lt;p&gt;这使用的是三路合并算法。不过我们在 &lt;a href=&quot;/post/git-merge-principle&quot;&gt;git 的合并原理（递归三路合并算法）&lt;/a&gt; 中说过，普通的三路合并算法会存在发现多个共同祖先的问题。此策略会“仔细地”寻找其中一个共同祖先。&lt;/p&gt;

&lt;p&gt;由于不需要递归合并出虚拟节点，所以此方法合并时会比较快速，但也可能会带来更多冲突。不敢说带来更多冲突是好事还是坏事，因为自动合并成功并不一定意味着在代码含义上也算是正确的合并。所以如果自动合并总是成功但代码含义上会失败，可以考虑此合并策略，这将让更多的冲突变成手工合并而不是自动合并。&lt;/p&gt;

&lt;h2 id=&quot;recursive&quot;&gt;recursive&lt;/h2&gt;

&lt;p&gt;这是默认的合并策略，如果你不指定策略参数，那么将使用这个合并策略。这将直接使用递归三路合并算法进行合并，详见：&lt;a href=&quot;/post/git-merge-principle&quot;&gt;git 的合并原理（递归三路合并算法）&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;当指定为此策略时，可以额外指定下面的这些参数，方法是：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge 要合并进来的分支名 &lt;span class=&quot;nt&quot;&gt;--strategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;合并策略 &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; diff-algorithm&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;参数
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge origin/master &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; recursive &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; diff-algorithm&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;patience
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;recursive&lt;/code&gt; 是默认的合并策略，所以可以简化成：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge origin/master &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; diff-algorithm&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;patience
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ours&quot;&gt;ours&lt;/h3&gt;

&lt;p&gt;如果不冲突，那么与默认的合并方式相同。但如果发生冲突，将自动应用自己这一方的修改。&lt;/p&gt;

&lt;p&gt;注意策略里面也有一个 ours，与这个不同的。&lt;/p&gt;

&lt;h3 id=&quot;theirs&quot;&gt;theirs&lt;/h3&gt;

&lt;p&gt;这与 ours 相反。如果不冲突，那么与默认的合并方式相同。但如果发生冲突，将自动应用来自其他人的修改（也就是 merge 参数中指定的那个分支的修改）。&lt;/p&gt;

&lt;h3 id=&quot;patience&quot;&gt;patience&lt;/h3&gt;

&lt;p&gt;此策略的名称叫“耐心”，因为 git 将话费更多的时间来进行合并一些看起来不怎么重要的行，合并的结果也更加准确。当然，使用的算法是 recursive 即递归三路合并算法。&lt;/p&gt;

&lt;p&gt;不过此名称也难以准确描述到底如何准确，不过可以举一个例子来说明：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一些省略的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Baz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一些省略的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后在这两个函数中增加另一个函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 一些省略的代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;默认情况下 git 会认为修改是这样的：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;+ }
+
+ int Bar()
+ {
+     // 一些省略的代码。
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;patience&lt;/code&gt; 策略后，git 将认为修改是这样的：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gi&quot;&gt;+ int Bar()
+ {
+     // 一些省略的代码。
+ }
+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你经常合并出现这些括号丢失或者符号不再匹配的问题，可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;patience&lt;/code&gt; 策略进行合并。&lt;/p&gt;

&lt;h3 id=&quot;no-renames&quot;&gt;no-renames&lt;/h3&gt;

&lt;p&gt;默认情况下 git 会识别出你重命名或者移动了文件，以便在你移动了文件之后依然可以与原文件进行合并。如果指定此策略，那么 git 将不再识别重命名，而是当作增加和删除了文件。&lt;/p&gt;

&lt;h3 id=&quot;其他的参数&quot;&gt;其他的参数&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;diff-algorithm=[patience|minimal|histogram|myers]&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;renormalize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;no-renormalize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;find-renames[=&amp;lt;n&amp;gt;]&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rename-threshold=&amp;lt;n&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;subtree[=&amp;lt;path&amp;gt;]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;octopus&quot;&gt;octopus&lt;/h2&gt;

&lt;p&gt;又是一个奇怪的名字——章鱼。章鱼有很多的触手，此合并策略就像这么多的触手一样。&lt;/p&gt;

&lt;p&gt;此策略允许合并多个 git 提交节点（分支）。不过，如果会出现需要手工解决的冲突，那么此策略将不会执行。&lt;/p&gt;

&lt;p&gt;此策略就是用来把多个分支聚集在一起的。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git merge t/lvyi t/walterlv &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; octopus
error: Merge requires file-level merging
Trying really trivial &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-index&lt;/span&gt; merge...
Nope.
Merge with strategy octopus failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ours-1&quot;&gt;ours&lt;/h2&gt;

&lt;p&gt;在合并的时候，无论有多少个合并分支，当前分支就直接是最终的合并结果。无论其他人有多少修改，在此次合并之后，都将不存在（当然历史里面还有）。&lt;/p&gt;

&lt;p&gt;你可能觉得这种丢失改动的合并策略没有什么用。但如果你准备重新在你的仓库中进行开发（程序员最喜欢的重构），那么当你的修改与旧分支合并时，采用此合并策略就非常有用，你新的重构代码将完全不会被旧分支的改动所影响。&lt;/p&gt;

&lt;p&gt;注意 recursive 策略中也有一个 ours 参数，与这个不同的。&lt;/p&gt;

&lt;h2 id=&quot;subtree&quot;&gt;subtree&lt;/h2&gt;

&lt;p&gt;此策略使用的是修改后的递归三路合并算法。与 recursive 不同的是，此策略会将合并的两个分支的其中一个视为另一个的子树，就像 git subtree 中使用的子树一样。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/using-branches/merge-strategy&quot;&gt;Git merge strategy options &amp;amp; examples - Atlassian Git Tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/47146379/any-example-to-use-git-merge-patience-strategy?noredirect=1&amp;amp;lq=1&quot;&gt;diff - Any example to use git merge patience strategy? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/merge-strategies&quot;&gt;Git - merge-strategies Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/366940/6233938&quot;&gt;When would you use the different git merge strategies? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/50359017/6233938&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-X patience&lt;/code&gt; vs &lt;code class=&quot;highlighter-rouge&quot;&gt;-X diff-algorithm=patience&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;git merge-recursive&lt;/code&gt; - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/23870235/6233938&quot;&gt;Git diff –patience not working - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Feb 2019 06:31:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-merge-strategy.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-merge-strategy.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>git 的合并原理（递归三路合并算法）</title>
        <description>&lt;p&gt;如果 git 只是一行行比较，然后把不同的行报成冲突，那么你在合并的时候可能会遇到大量的冲突；这显然不是一个好的版本管理工具。&lt;/p&gt;

&lt;p&gt;本文介绍 git 合并分支的原理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;git-的冲突表示&quot;&gt;git 的冲突表示&lt;/h2&gt;

&lt;p&gt;例如我们有这样的三个提交 a、b、c。a、b 是在 master 上的其他修改，c 是我自己基于 master 上的 a 的修改。&lt;/p&gt;

&lt;p&gt;现在，将 master 分支合并到我自己的 t/walterlv 分支：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-12-21-09-19.png&quot; alt=&quot;git 提交树&quot; /&gt;&lt;/p&gt;

&lt;p&gt;a 提交：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;b 提交：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello Master!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;c 提交：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello Walterlv!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是现在将 c 提交合并到 master 的时候就会出现冲突。冲突的表示会是这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Console.WriteLine(&quot;Hello Walterlv!&quot;);
=======
Console.WriteLine(&quot;Hello Master!&quot;);
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt; 表示冲突开头，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; 表示冲突结尾，&lt;code class=&quot;highlighter-rouge&quot;&gt;=======&lt;/code&gt; 分隔冲突的不同修改。上面是 HEAD，也就是在合并之前的工作目录上的最近提交；下面是合并进来的分支，通常是来自其他人的修改。&lt;/p&gt;

&lt;h2 id=&quot;三路合并&quot;&gt;三路合并&lt;/h2&gt;

&lt;p&gt;加入上面的 b 提交修改的是其他文件。然后依然按照前面的方式进行合并。&lt;/p&gt;

&lt;p&gt;当出现冲突时，如果你只能看到不同的两行，那么你根本不知道究竟应该如何修改的。就像下面这样：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Console.WriteLine(&quot;Hello Walterlv!&quot;);
=======
Console.WriteLine(&quot;Hello World!&quot;);
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只看这点你怎么知道两行应该采用哪一行？这是二路合并算法带来的问题。在此算法下，你的每次拉取代码可能都会带来大量的冲突；这显然是不能接受的。&lt;/p&gt;

&lt;p&gt;三路合并算法会找到合并的这两个提交的共同祖先。在这里也就是 a 提交。master 的此文件对 a 没有修改，而当前分支 t/walterlv 对此文件有修改，于是就会应用此分支的修改。&lt;/p&gt;

&lt;p&gt;当然，前一节的问题依然会冲突，因为两个分支相对于共同的祖先节点 a 对同一个文件都有修改。&lt;/p&gt;

&lt;h2 id=&quot;递归三路合并&quot;&gt;递归三路合并&lt;/h2&gt;

&lt;p&gt;从上面我们可以看到三路合并解决了二路合并中对于相同行不知道用哪一个的问题。不过实际的 git 提交树会更加复杂，就像下图那样纵横交错：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-02-14-20-44-32.png&quot; alt=&quot;纵横交错的 git 提交树&quot; /&gt;&lt;/p&gt;

&lt;p&gt;相比于本文一开始，我们只是新增了两个提交而已，现在 f 提交是我们正在合并的提交。&lt;/p&gt;

&lt;p&gt;如果现在找 e 和 d 的共同祖先，你会发现并不唯一，b 和 c 都是。那么此时怎么合并呢？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;git 会首先将 b 和 c 合并成一个虚拟的提交 x，这个 x 当作 e 和 d 的共同祖先。&lt;/li&gt;
  &lt;li&gt;而要合并 b 和 c，也需要进行同样的操作，即找到一个共同的祖先 a。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们这里的 a、b、c 只是个比较简单的例子，实际上提交树往往更加复杂，这就需要不断重复以上操作以便找到一个真实存在的共同祖先，而这个操作是递归的。这便是“递归三路合并”的含义。&lt;/p&gt;

&lt;p&gt;这是 git 合并时默认采用的策略。&lt;/p&gt;

&lt;h2 id=&quot;快进式合并&quot;&gt;快进式合并&lt;/h2&gt;

&lt;p&gt;git 还有非常简单的快进式（Fast-Forward）合并。快进式合并要求合并的两个分支（或提交）必须是祖孙/父子关系。例如上面的 e 和 d 并不满足此关系，所以无法进行快进式合并。&lt;/p&gt;

&lt;p&gt;在上面的例子合并出了 f 之后，如果将 t/walterlv 合并到 master，那么就可以使用快进式合并。这时，直接将 master 分支的 HEAD 指向 f 提交即完成了合并。当然，可以生成也可以不生成新的 g 提交，但内容与 f 的内容完全一样。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/4129049/6233938&quot;&gt;version control - Why is a 3-way merge advantageous over a 2-way merge? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.guiffy.com/SureMergeWP.html&quot;&gt;Guiffy SureMerge - A Trustworthy 3-Way Merge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/11133290/6233938&quot;&gt;git merge - Which version of the git file will be finally used: LOCAL, BASE or REMOTE? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/using-branches/merge-strategy&quot;&gt;Git merge strategy options &amp;amp; examples - Atlassian Git Tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.unix.com/man-page/linux/1/git-merge-base/&quot;&gt;git-merge-base (1) - Find as good common ancestors as possible for a merge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 14 Feb 2019 13:03:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-merge-principle.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-merge-principle.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>将 .NET Core 项目打一个最简单的 NuGet 源码包，安装此包就像直接把源码放进项目一样</title>
        <description>&lt;p&gt;NuGet 原本就提供了生成源码包的功能。不过，NuGet 原生的源码包仅用于调试时自带调试信息和调试源码。&lt;/p&gt;

&lt;p&gt;本文将以最简单的方式制作一个源码引用包。安装 NuGet 包后，不会生成任何程序集引用，而是相当于将源码直接放入被安装的程序集中一样。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;我们需要一个可以用来打 NuGet 包的 .NET Core 项目，只需要在 Visual Studio 中新建一个即可。在本例中，我的项目名字是 Walterlv.Demo。&lt;/p&gt;

&lt;h2 id=&quot;将源码加入-nuget-包&quot;&gt;将源码加入 NuGet 包&lt;/h2&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/known-nuget-properties-in-csproj&quot;&gt;项目文件中的已知 NuGet 属性（使用这些属性，创建 NuGet 包就可以不需要 nuspec 文件啦）&lt;/a&gt; 中，我说到了项目文件中的各种 NuGet 属性。在本文中，我们将使用到其中的一部分。&lt;/p&gt;

&lt;p&gt;这些属性将设置到项目文件 Walterlv.Demo.csproj 中。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 将源码引入包中。 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeSource&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeSource&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果指定为 true，那么生成的 dll 将拷贝到 NuGet 包的 tools 目录下。 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;IsTool&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsTool&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了避免将打出来的 NuGet 包作为 dll 被安装的程序集引用，我们需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;IsTool&amp;gt;true&amp;lt;/IsTool&amp;gt;&lt;/code&gt; 属性。这样，生成的 dll 将只会放入 &lt;code class=&quot;highlighter-rouge&quot;&gt;tools&lt;/code&gt; 文件夹中，而不会被引用。&lt;/p&gt;

&lt;p&gt;这时，项目的 csproj 文件像这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeSource&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeSource&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoPackageAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoPackageAnalysis&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsTool&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsTool&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;设置后编译项目，我们将在输出目录得到 Walterlv.Demo.nupkg 和 Walterlv.Demo.1.0.0.symbols.nupkg 两个文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-08-36-11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这种带后缀形式的包在只是 NuGet 的辅助包而已，不是主包。在 &lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages?wt.mc_id=MVP&quot;&gt;How to create NuGet symbol packages - Microsoft Docs&lt;/a&gt; 中说明这种 symbols 的包只是用于调试的。然而，我们需要的是将其作为直接引用的主包。这种情况下，Walterlv.Demo.nupkg 因为不能满足我们的目的，所以我们并不能使用它。所以，我们需要做的是，将 Walterlv.Demo.1.0.0.symbols.nupkg 变成主包。&lt;/p&gt;

&lt;p&gt;于是，我们编写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt; 将 symbols 包替换主包：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeSource) == 'True' Or $(IncludeSymbols) == 'True'&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UseSymbolsInsteadOfLib&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenerateNuspec&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Delete&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Files=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Move&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).symbols.nupkg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里使用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Delete /&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Move /&amp;gt;&lt;/code&gt; 两个自带的 Task，用于将功能不全的主包删除，然后将我们的源码包替换成为主包。我此前写过 &lt;a href=&quot;/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target&lt;/a&gt; 介绍了一些自带的 Task。如果你想了解更多 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt; 编写相关的知识，也可以阅读这篇文章。&lt;/p&gt;

&lt;p&gt;在增加了上面的一段 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt; 之后，最终我们将只会得到一个 NuGet 包，打开后能发现其中包含源码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-09-15-55.png&quot; alt=&quot;生成的包&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装-nuget-包时引入源码&quot;&gt;安装 NuGet 包时引入源码&lt;/h2&gt;

&lt;p&gt;为了让源码能随着包的安装加入到目标项目，我们需要 targets 文件来将源码引入。&lt;/p&gt;

&lt;p&gt;在项目中新建 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 文件夹，这将用来放即将存入 NuGet 包中的文件。新建 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assets\build\Package.targets&lt;/code&gt; 文件，这个文件会被自动引入到被安装的项目中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-09-14-56.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是我们在 csproj 中额外添加一些代码将这个文件在打包时改名为正确的名称。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\*.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，整个 csproj 文件看起来是这样：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netstandard2.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;GeneratePackageOnBuild&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/GeneratePackageOnBuild&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeSource&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeSource&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoPackageAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoPackageAnalysis&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsTool&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsTool&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DevelopmentDependency&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DevelopmentDependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\build\*.targets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;build\$(PackageId).targets&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeSource) == 'True'&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UseSymbolsInsteadOfLib&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenerateNuspec&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Delete&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Files=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Move&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SourceFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).symbols.nupkg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DestinationFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而至于我们刚刚新建的 Package.targets 文件中，我们也需要为目标项目填写一些内容：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoIncludeSource&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\src\Walterlv.Demo\**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\src\Walterlv.Demo\**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，一旦目标程序集安装了这个 NuGet 包，便会将所有的 cs 文件加入到目标项目的编译中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-09-21-56.png&quot; alt=&quot;不会出现编译错误&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 30 Jan 2019 14:33:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-simplest-way-to-pack-a-source-code-nuget-package.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-simplest-way-to-pack-a-source-code-nuget-package.html</guid>
        
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>ReSharper 在 C 盘占用了太多空间了，本文告诉你如何安全地删除或转移这些文件</title>
        <description>&lt;p&gt;一个不小心，我的 SSD 又满了。到底是谁占用了那么多的空间！如果你是 ReSharper 的重度用户，那么可能你的调查结果会直指 JetBrains ReSharper。&lt;/p&gt;

&lt;p&gt;本文将告诉你如何安全地删除这些文件来释放你的 C 盘空间，然后在 ReSharper 中设置其他的缓存目录。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;消失的-c-盘空间&quot;&gt;消失的 C 盘空间&lt;/h2&gt;

&lt;p&gt;SSD 很贵的，看看都满成什么样儿了……我一个 SSD 分成了 C 和 D 两个分区，都满了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-20-23-31.png&quot; alt=&quot;近乎满了的 SSD&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可以使用 SpaceSniffer 来快速调查占用你大量 C 盘空间的到底是些什么文件。我之前写过一篇文章介绍如何使用它：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/windows/2017/09/17/find-lost-space-using-space-sniffer.html&quot;&gt;找回你 C 盘丢失的空间（SpaceSniffer）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当你是 ReSharper 的重度用户的时候，你很有可能会看到如下的场景：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-19-58-03.png&quot; alt=&quot;JetBrains 家的软件竟然占据了这么多空间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;是的，JetBrains 家的软件竟然占用了 17.2GB 的 C 盘空间！他们一定认为所有的用户都是土豪，能够买 500GB 以上的 SSD 全部分配给 C 盘。&lt;/p&gt;

&lt;p&gt;好的，吐槽就到这里，我们进入正题——删除这些文件。&lt;/p&gt;

&lt;h2 id=&quot;删除-resharper-的缓存目录&quot;&gt;删除 ReSharper 的缓存目录&lt;/h2&gt;

&lt;p&gt;注意：&lt;strong&gt;只有 Transient 文件夹是可以删除的&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;ReSharper 安装时的目录都在 &lt;code class=&quot;highlighter-rouge&quot;&gt;%LocalAppData%\JetBrains&lt;/code&gt; 中。虽然运行时的缓存也在这里，但是如果你直接把这个目录删掉了，那么 ReSharper 插件以及 JetBrains 全家桶也就不能正常使用了。&lt;/p&gt;

&lt;p&gt;Transient 意思跟 Temporary 差不多，就是短暂使用的文件。不过 ReSharper 竟然在这里堆了这么多。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-20-10-46.png&quot; alt=&quot;Transient&quot; /&gt;&lt;/p&gt;

&lt;p&gt;删除掉这个文件夹不影响 ReSharper 及其他 JetBrains 全家桶的正常运行。&lt;/p&gt;

&lt;p&gt;ReSharper 在设置中提供了清除缓存的按钮，但那个按钮点了其实释放不了多少空间的，本文最后一句将说明这个问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-20-34-07.png&quot; alt=&quot;删除 Transient 目录&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;转移-resharper-的缓存目录&quot;&gt;转移 ReSharper 的缓存目录&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;从 Visual Studio 的菜单中进入 ReSharper 的设置界面：ReSharper -&amp;gt; Options；&lt;/li&gt;
  &lt;li&gt;进入缓存设置选项：Environment -&amp;gt; General -&amp;gt; Caches -&amp;gt; Store solution。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在这里可以修改 ReSharper 缓存文件的存储位置。&lt;/p&gt;

&lt;p&gt;不过可得提醒你一下，ReSharper 这么耗性能的插件，还是老老实实放 SSD 里面吧，SSD 再怎么贵比起你的时间来说可便宜多了呀！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-20-27-21.png&quot; alt=&quot;ReSharper Options&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-30-20-28-18.png&quot; alt=&quot;更改缓存目录&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以在这个界面中看到，ReSharper 其实是提供了清除缓存的按钮（Clear）的，但是这个按钮点击之后其实只是会删除当前项目的缓存。而实际上 ReSharper 在你的电脑上积攒久了是众多缓存文件一起占用的太多空间，只删除最近正在使用的这个项目其实根本释放不了多少空间的。（比如我打开我的 Walterlv.CloudKeyboard 项目清除结果只删掉了不到 100M 的空间。）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://resharper-support.jetbrains.com/hc/en-us/community/posts/360000087690-Remove-old-caches&quot;&gt;Remove old caches – ReSharper Support - JetBrains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 30 Jan 2019 12:34:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/safely-remove-jetbrains-resharper-cache-folder.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/safely-remove-jetbrains-resharper-cache-folder.html</guid>
        
        
        <category>dotnet</category>
        
        <category>resharper</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>WPF's multi-threaded UI is not thread safe</title>
        <description>&lt;p&gt;WPF supports multiple UI threads in its framework. You can create multiple UI thread windows or create multiple UI threads in a single window. But unfortunately, this is not really thread-safe.&lt;/p&gt;

&lt;p&gt;There is a very low probability that WPF application will crash when you creating a multi-thread UI. In this post, I’ll tell how this happens.&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-issue&quot;&gt;The Issue&lt;/h2&gt;

&lt;p&gt;Necessary conditions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create multiple WPF UI threads
    &lt;ul&gt;
      &lt;li&gt;In fact, two are enough, one is the main UI thread with the App class we usually write; a background UI thread, for example, to display the UI thread that starts the splash screen.&lt;/li&gt;
      &lt;li&gt;If you use two threads, you need a lot of repetitive trials to reproduce; and by creating more threads you can greatly improve the probability of a single recurrence&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;These UI threads all display WPF windows&lt;/li&gt;
  &lt;li&gt;This issue will occur in both WPF on .NET Core 3 and WPF on .NET Framework 4.7.2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;phenomenon:&lt;/p&gt;

&lt;p&gt; - An exception is thrown and the application crashes&lt;/p&gt;

&lt;p&gt;For example, the following is one of the exceptions:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Exception thrown: 'System.NullReferenceException' in WindowsBase.dll
Object reference not set to an instance of an object.

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.IO.Packaging.PackagePart.CleanUpRequestedStreamsList()
   at System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess access)
   at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
   at Walterlv.Bugs.MultiThreadedUI.SplashWindow.InitializeComponent() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml:line 1
   at Walterlv.Bugs.MultiThreadedUI.SplashWindow..ctor() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml.cs:line 24
   at Walterlv.Bugs.MultiThreadedUI.Program.&amp;lt;&amp;gt;c__DisplayClass1_0.&amp;lt;RunSplashWindow&amp;gt;b__0() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\Program.cs:line 33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following image is an exception caught in WPF on .NET Core 3 that is shown in visual studio 2019:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-29-11-04-38.png&quot; alt=&quot;The exception&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-reproduce&quot;&gt;How to Reproduce&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new WPF project (either .NET Core 3 or .NET Framework 4.7.2)&lt;/li&gt;
  &lt;li&gt;Keep the automatically generated &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; unchanged, we create a new window &lt;code class=&quot;highlighter-rouge&quot;&gt;SplashWindow&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Create a new &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; class containing the Main function and set &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; as the startup object (instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt;) in the project properties.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-29-11-06-01.png&quot; alt=&quot;The project structure&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All other files remain the same as the default code generated by Visual Studio, and the code of Program.cs is as follows:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Bugs.MultiThreadedUI&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;RunSplashWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunSplashWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SplashWindow&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;SplashWindow &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;IsBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remarks: Even if you add this code just before the Splash Window creating, this exception still occurs.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 29 Jan 2019 04:10:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-multi-thread-ui-is-not-thread-safe-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-multi-thread-ui-is-not-thread-safe-en.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 支持的多线程 UI 并不是线程安全的</title>
        <description>&lt;p&gt;WPF 支持创建多个 UI 线程，跨窗口的或者窗口内的都是可以的；但是这个过程并不是线程安全的。&lt;/p&gt;

&lt;p&gt;你有极低的概率会遇到 WPF 多线程 UI 的线程安全问题，说直接点就是崩溃。本文将讲述其线程安全问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;简述这个线程安全问题&quot;&gt;简述这个线程安全问题&lt;/h2&gt;

&lt;p&gt;必要条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建多个 WPF UI 线程
    &lt;ul&gt;
      &lt;li&gt;其实两个就够了，一个我们平时写的 App 类所在的主 UI 线程；一个后台 UI 线程，例如用来显示启动闪屏的 UI 线程&lt;/li&gt;
      &lt;li&gt;两个线程的话你需要大量重复试验才能复现；而创建更多线程可以大大提高单次复现概率&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;这些 UI 线程都显示 WPF 窗口&lt;/li&gt;
  &lt;li&gt;无论是 .NET Framework 4.7.2 版本的 WPF，还是 .NET Core 3 版本的 WPF 都会出现此问题&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;现象：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;抛出异常，程序崩溃&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;比如下面是其中一种异常：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Exception thrown: 'System.NullReferenceException' in WindowsBase.dll
Object reference not set to an instance of an object.

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.IO.Packaging.PackagePart.CleanUpRequestedStreamsList()
   at System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess access)
   at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
   at Walterlv.Bugs.MultiThreadedUI.SplashWindow.InitializeComponent() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml:line 1
   at Walterlv.Bugs.MultiThreadedUI.SplashWindow..ctor() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml.cs:line 24
   at Walterlv.Bugs.MultiThreadedUI.Program.&amp;lt;&amp;gt;c__DisplayClass1_0.&amp;lt;RunSplashWindow&amp;gt;b__0() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\Program.cs:line 33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下图是 .NET Core 3 版本的 WPF 中在 Visual Studio 2019 抓到的异常：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-29-11-04-38.png&quot; alt=&quot;异常&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;复现步骤&quot;&gt;复现步骤&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;创建一个新的 WPF 项目（无论是 .NET Framework 4.7.2 还是 .NET Core 3）&lt;/li&gt;
  &lt;li&gt;保持自动生成的 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 不变，我们额外创建一个窗口 &lt;code class=&quot;highlighter-rouge&quot;&gt;SplashWindow&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;创建一个新的包含 Main 函数的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类，并在项目属性中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program&lt;/code&gt; 为启动对象（替代 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt;）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-29-11-06-01.png&quot; alt=&quot;项目结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其他文件全部保持 Visual Studio 生成的默认代码不变，而 Program.cs 的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Bugs.MultiThreadedUI&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;RunSplashWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunSplashWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SplashWindow&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;SplashWindow &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;IsBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;说明：即便在 &lt;code class=&quot;highlighter-rouge&quot;&gt;new SplashWindow&lt;/code&gt; 代码之前调用以下方法修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 也依然会发生异常。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 29 Jan 2019 04:02:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-multi-thread-ui-is-not-thread-safe.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-multi-thread-ui-is-not-thread-safe.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET/C# 编译期间能确定的相同字符串，在运行期间是相同的实例</title>
        <description>&lt;p&gt;我们知道，在编译期间相同的字符串，在运行期间就会是相同的字符串实例。然而，如果编译期间存在字符串的运算，那么在运行期间是否是同一个实例呢？&lt;/p&gt;

&lt;p&gt;只要编译期间能够完全确定的字符串，就会是同一个实例。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;字符串在编译期间能确定的运算包括：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;A + B&lt;/code&gt; 即字符串的拼接&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$&quot;{A}&quot;&lt;/code&gt; 即字符串的内插&lt;/li&gt;
&lt;/ol&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;字符串拼接&quot;&gt;字符串拼接&lt;/h2&gt;

&lt;p&gt;对于拼接，我们不需要运行便能知道是否是同一个实例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv is a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上这段代码是可以编译通过的，因为能够写为 &lt;code class=&quot;highlighter-rouge&quot;&gt;const&lt;/code&gt; 的字符串，一定是编译期间能够确定的。&lt;/p&gt;

&lt;h2 id=&quot;字符串内插&quot;&gt;字符串内插&lt;/h2&gt;

&lt;p&gt;对于字符串内插，以上代码我们不能写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;const&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-14-42-00.png&quot; alt=&quot;错误提示&quot; /&gt;&lt;/p&gt;

&lt;p&gt;错误提示为：常量的初始化必须使用编译期间能够确定的常量。&lt;/p&gt;

&lt;p&gt;然而，这段代码不能在编译期间确定吗？实际上我们有理由认为编译器其实是能够确定的，只是编译器这个阶段没有这么去做而已。&lt;/p&gt;

&lt;p&gt;实际上在 2017 年就有人在 GitHub 上提出了这个问题，你可以在这里看讨论：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/2077&quot;&gt;[Discussion] Constant string interpolation · Issue #2077 · dotnet/csharplang&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/384&quot;&gt;String interpolation constants · Issue #384 · dotnet/csharplang&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/issues/11259&quot;&gt;[Discussion] Constant string interpolation · Issue #11259 · dotnet/roslyn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，我们写一个程序来验证这是否是同一个实例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;walterlv is a &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;walterlv is a &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;walterlv is a &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;walterlv is a &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;H&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的输出为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;False
False
False
True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，对于最后一种情况，也就是内插的字符串是常量的时候，得到的字符串是同一个实例；这能间接证明编译期间完全确定了字符串 G。&lt;/p&gt;

&lt;p&gt;注意，其他情况都不能完全确定：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;属性内插时一定不确定；&lt;/li&gt;
  &lt;li&gt;静态字段内插时，无论是否是只读的，都不能确定。（谁知道有没有人去反射改掉呢？）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们可以通过 IL 来确定前面的间接证明（代码太长，我只贴出来最重要的 G 字符串，以及一个用来比较的 E 字符串）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.method private hidebysig static specialname string
    get_G() cil managed
{
    .maxstack 8

    // [22 36 - 22 56]
    IL_0000: ldstr        &quot;walterlv is a 逗比&quot;
    IL_0005: ret

}
.method private hidebysig static specialname string
    get_E() cil managed
{
    .maxstack 8

    // [20 36 - 20 56]
    IL_0000: ldstr        &quot;walterlv is a &quot;
    IL_0005: ldsfld       string Walterlv.Demo.Roslyn.Program::F
    IL_000a: call         string [System.Runtime]System.String::Concat(string, string)
    IL_000f: ret

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以发现，实际上 G 已经在编译期间完全确定了。&lt;/p&gt;

&lt;h2 id=&quot;扩展修改编译期间的字符串&quot;&gt;扩展：修改编译期间的字符串&lt;/h2&gt;

&lt;p&gt;前面我们说到可以在编译期间完全确定的字符串。呃，为什么一定要抬杠额外写一节呢？&lt;/p&gt;

&lt;p&gt;下面我们修改编译期间确定的字符串，看看会发生什么：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里的 G 就是前面定义的那个 G。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fixed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'W'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行结果是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv is a 逗比
walterlv is a 逗比
Walterlv is a 逗比
Walterlv is a 逗比
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;虽然我们看起来只是在修改我们自己局部定义的一个字符串，但是实际上已经修改了另一个常量以及属性 G。&lt;/p&gt;

&lt;p&gt;少年，使用指针修改字符串是很危险的！鬼知道你会把程序改成什么样！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated&quot;&gt;$ - string interpolation - C# Reference - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/2077&quot;&gt;[Discussion] Constant string interpolation · Issue #2077 · dotnet/csharplang&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/384&quot;&gt;String interpolation constants · Issue #384 · dotnet/csharplang&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/issues/11259&quot;&gt;[Discussion] Constant string interpolation · Issue #11259 · dotnet/roslyn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 25 Jan 2019 07:47:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/same-strings-at-compile-time-are-the-same-instances-at-runtime.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/same-strings-at-compile-time-are-the-same-instances-at-runtime.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 Xamarin 开发 iOS 应用中需要注意的若干个问题</title>
        <description>&lt;p&gt;本文收集整理使用 Xamarin 开发 iOS 应用时可能会遇到的各种问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;需要注册-apple-developer-portal&quot;&gt;需要注册 Apple Developer Portal&lt;/h2&gt;

&lt;p&gt;不管你用什么开发 iOS 应用，成为一个 Apple 的开发者是必要的。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;访问：&lt;a href=&quot;https://developer.apple.com/register/&quot;&gt;https://developer.apple.com/register/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;登录&lt;/li&gt;
  &lt;li&gt;同意协议&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;完成！虽然简单，但是如果没有成为开发者，那么你在所有工具上都无法成功部署应用。&lt;/p&gt;

&lt;h2 id=&quot;could-not-find-any-available-provisioning-profiles-for-ios&quot;&gt;Could not find any available provisioning profiles for iOS&lt;/h2&gt;

&lt;p&gt;这个错误可能出现在你是用 Visual Studio 或者 Visual Studio for Mac 部署真机调试的时候出现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;只有 XCode 才能生成 provisioning profiles&lt;/strong&gt;！所以，如果你希望只使用 Visual Studio 或者 Visual Studio For Mac 或者 Xamarin 来部署是不可能的。&lt;/p&gt;

&lt;p&gt;如果出现了此错误，你需要使用 XCode 提前生成一份 provisioning profiles 然后在 Visual Studio 中使用这份 profiles。&lt;/p&gt;

&lt;p&gt;方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 XCode 中新建一个项目；&lt;/li&gt;
  &lt;li&gt;填写 Bundle Identifier：
    &lt;ul&gt;
      &lt;li&gt;注意：必须写成跟你待会儿用 Visual Studio 部署时项目一模一样的 Bundle Identifier！&lt;/li&gt;
      &lt;li&gt;比如你在 Visual Studio for Mac 中准备部署的应用为 &lt;code class=&quot;highlighter-rouge&quot;&gt;com.walterlv.CloudKeyboard&lt;/code&gt;，那么在这里也必须填写 &lt;code class=&quot;highlighter-rouge&quot;&gt;com.walterlv.CloudKeyboard&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;在 XCode 中部署这个临时的项目；
    &lt;ul&gt;
      &lt;li&gt;你必须确保真的成功部署到真机上了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;换回 Visual Studio，理论上你现在就可以成功部署了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至于那个在 XCode 中临时建的项目，你可以丢掉，也可以留着。毕竟这种方式创建的 provisioning profiles 只有 6 天的有效期。如果过期了，你就需要再来一次。&lt;/p&gt;

&lt;p&gt;如果依然不能部署，你需要去项目中设置一下，Visual Studio 中的设置方法如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-21-15-19-55.png&quot; alt=&quot;设置 Provisioning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio for Mac 中的设置方法则是选中这个项目的 Info.plist 文件，然后点击 Bundle Signing，在对话框中选。&lt;/p&gt;

&lt;h2 id=&quot;需要注册-apple-developer-program&quot;&gt;需要注册 Apple Developer Program&lt;/h2&gt;

&lt;p&gt;注意，注册 Apple Developer Program 需要付 $99 美元的年费。&lt;/p&gt;

&lt;p&gt;即便没有注册，也可以部署真机调试，但如上文所说，只有 6 天的有效期。如果注册了，那么有一年。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44321291/how-to-check-whether-xcode-downloaded-all-profiles&quot;&gt;How to check whether Xcode downloaded all profiles? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 22 Jan 2019 01:11:53 +0000</pubDate>
        <link>https://blog.walterlv.com/post/tips-for-developing-xamarin-ios-app.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/tips-for-developing-xamarin-ios-app.html</guid>
        
        
        <category>xamarin</category>
        
        <category>ios</category>
        
      </item>
    
      <item>
        <title>C# 永远不会返回的方法真的不会返回</title>
        <description>&lt;p&gt;一般情况下，如果一个方法声明了返回值，但是实际上在编写代码的时候没有返回，那么这个时候会出现编译错误。&lt;/p&gt;

&lt;p&gt;然而，如果方法内部出现了永远也不会退出的死循环，那么这个时候就不会出现编译错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;请看下面这一段代码，&lt;code class=&quot;highlighter-rouge&quot;&gt;RunAndNeverReturns&lt;/code&gt; 方法声明了返回值 &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; 但实际上方法内部没有返回。这段代码是可以编译通过而且可以正常运行的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;RunAndNeverReturns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAndNeverReturns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv will always appear.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 注意看，这个方法其实没有返回。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果观察其 IL 代码，会发现此方法的 IL 代码里面是没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;ret&lt;/code&gt; 语句的。而其他正常的方法，即便返回值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;void&lt;/code&gt;，也是有 &lt;code class=&quot;highlighter-rouge&quot;&gt;ret&lt;/code&gt; 语句的。&lt;/p&gt;
</description>
        <pubDate>Sun, 20 Jan 2019 07:36:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/method-that-never-returns-does-need-not-return.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/method-that-never-returns-does-need-not-return.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>CentOS 的终端中如何搜索文件</title>
        <description>&lt;p&gt;CentOS 中搜索文件可以使用 find 命令。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果需要在当前文件夹中搜索文件，那么可以使用命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;find &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; filename
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中 filename 是你需要找的文件或文件夹的名称。我们没有指定搜索文件的路径，默认是当前文件夹。&lt;/p&gt;

&lt;p&gt;如果你希望在所有文件夹中查找，那么可以使用命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;find / &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; filename
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 是根目录的意思，当然，你也可以指定为其他路径。&lt;/p&gt;

&lt;p&gt;比如我要搜索 dotnet 的 SDK，可以使用：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;find / &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; dotnet
/usr/share/dotnet
/usr/share/dotnet/dotnet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回了两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; 文件夹。&lt;/p&gt;

&lt;p&gt;也可以使用通配符：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;find / &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/21046448/6233938&quot;&gt;linux - How to search for a file in the CentOS command line - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 20 Jan 2019 07:05:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/search-file-in-the-centos-command-line.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/search-file-in-the-centos-command-line.html</guid>
        
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>不使用 U 盘等任何工具全新安装 Windows 操作系统</title>
        <description>&lt;p&gt;安装 Windows 有非常多种方法，现在我们要解决的问题是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;手头没有量产的 U 盘，或者懒得花时间去用 iso 文件量产 U 盘；&lt;/li&gt;
  &lt;li&gt;不想在 Windows 现有系统下安装（可能是为了全新安装，也可能是为了跳过安装序列号/产品密钥）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;于是本文教你如何一步一步在 Windows RE 环境下安装操作系统。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Windows 10 的安装文件
    &lt;ul&gt;
      &lt;li&gt;例如 cn_windows_10_consumer_editions_version_1809_updated_jan_2019_x64_dvd_34b4d4fb.iso&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;现有系统是 Windows 8/8.1/10 操作系统&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;第一步解压-iso-文件&quot;&gt;第一步：解压 iso 文件&lt;/h2&gt;

&lt;p&gt;将 iso 文件解压到一个文件夹中，例如，我解压到 D:\Windows10 文件夹中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-33-15.png&quot; alt=&quot;解压 iso 到一个文件夹中&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步重启进入-re-环境&quot;&gt;第二步：重启进入 RE 环境&lt;/h2&gt;

&lt;p&gt;现在，在开始菜单中点击电源按钮，这时会弹出电源选择菜单。注意：&lt;strong&gt;请按住 Shift 键不放，然后点击重启按钮&lt;/strong&gt;，重启按钮点完之后才能松开 Shift 键。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-34-56.png&quot; alt=&quot;按住 Shift 键点击重启按钮&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步等待进入-re-环境&quot;&gt;第三步：等待进入 RE 环境&lt;/h2&gt;

&lt;p&gt;这时重启会进入 RE 环境。Windows RE 指的是 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-recovery-environment--windows-re--technical-reference&quot;&gt;Windows Recovery Environment&lt;/a&gt;，也就是 Windows 恢复环境。你可以在这里进行很多系统之外的操作。相比于 PE 需要一个光盘或者 U 盘来承载，RE 是直接在你安装 Windows 8/8.1/10 时直接自带到机器硬盘上的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-38-03.png&quot; alt=&quot;进入 RE 环境&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第四步进入-re-环境的命令提示符&quot;&gt;第四步：进入 RE 环境的命令提示符&lt;/h2&gt;

&lt;p&gt;依次进入 疑难解答 -&amp;gt; 高级选项 -&amp;gt; 命令提示符 -&amp;gt; 选择自己的账号 -&amp;gt; 输入自己的密码&lt;/p&gt;

&lt;p&gt;注意，在选择命令提示符之后，计算机还会再重启一次，所以需要等一会儿才会到选择账号的界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-44-59.png&quot; alt=&quot;疑难解答&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-46-03.png&quot; alt=&quot;高级选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-47-02.png&quot; alt=&quot;命令提示符&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-52-19.png&quot; alt=&quot;选择自己的账号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-53-46.png&quot; alt=&quot;输入自己账号的密码&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第五步在命令提示符中找到安装程序&quot;&gt;第五步：在命令提示符中找到安装程序&lt;/h2&gt;

&lt;p&gt;我们一开始将系统解压到了 D:\Windows10 文件夹下。一般来说，现在也应该是在 D 盘的 Windows10 文件夹下。不过有时候你会发现这里的 D 盘并不是你想象中那个 D 盘，你找不到那个文件夹和里面那个安装文件。这个时候可以去 C 盘、E 盘、F 盘等地方也看看。&lt;/p&gt;

&lt;p&gt;命令提示符的操作这里就不赘述了，无非是 &lt;code class=&quot;highlighter-rouge&quot;&gt;D:&lt;/code&gt; 跳转到某个盘符，&lt;code class=&quot;highlighter-rouge&quot;&gt;cd&lt;/code&gt; 跳转到某个文件夹下，&lt;code class=&quot;highlighter-rouge&quot;&gt;setup.exe&lt;/code&gt; 打开 setup.exe 这个程序。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-19-57-34.png&quot; alt=&quot;打开 setup.exe&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第六步按照熟悉的安装系统的流程安装操作系统&quot;&gt;第六步：按照熟悉的安装系统的流程安装操作系统&lt;/h2&gt;

&lt;p&gt;现在，你应该可以看到熟悉的 Windows 10 安装界面了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-20-08-31.png&quot; alt=&quot;开始安装 Windows&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如，你可以在这里跳过产品密钥的输入：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-20-09-59.png&quot; alt=&quot;跳过产品密钥的输入&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-20-10-45.png&quot; alt=&quot;选择 Windows 10 的安装版本&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如可以使用在 Windows 内部安装无法使用的“自定义”安装方式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-20-11-17.png&quot; alt=&quot;使用自定义的安装方式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;甚至能在这里格式化所有分区，删除所有磁盘：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-17-20-12-58.png&quot; alt=&quot;格式化分区或者删除磁盘&quot; /&gt;&lt;/p&gt;

&lt;p&gt;剩下的，祝你好运！&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Jan 2019 12:13:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/install-windows-via-re-without-any-other-external-tools.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/install-windows-via-re-without-any-other-external-tools.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>C#/.NET 如何确认一个路径是否是合法的文件路径</title>
        <description>&lt;p&gt;很多方法要求传入一个字符串作为文件名或者文件路径，不过方法在实际执行到使用文件名的时候才会真正使用到这个文件名；于是这这种时候才会因为各种各样的异常发现文件名或者文件路径是不合法的。&lt;/p&gt;

&lt;p&gt;有没有方法能够提前验证文件名或者文件路径是否是合法的路径呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;这是一个不幸的结论 —— 没有！&lt;/p&gt;

&lt;p&gt;实际上由我们自己写代码判断一个字符串是否是一个合法的文件路径是非常困难的，因为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不同操作系统的路径格式是不同的；&lt;/li&gt;
  &lt;li&gt;同一个操作系统有各种各样不同的路径用途。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;但你可能会说，就算有各种不同，也是可以穷举出来的。那么来看看穷举这些不同的情况需要多少代码吧：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/Path.Windows.cs&quot;&gt;Path.Windows.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs&quot;&gt;PathHelper.Windows.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs&quot;&gt;PathInternal.Windows.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;看完这些代码，你是不是可以考虑放弃做 100% 精确的提前验证了？放弃是正解。&lt;/p&gt;

&lt;p&gt;那么接下来如何验证呢？&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;new FileInfo(string fileName)&lt;/code&gt; 类型和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetFullPath(string path)&lt;/code&gt; 方法来判断，则会使用到以上的代码，不过副作用是在路径不合法的时候抛出异常。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-16-20-14-28.png&quot; alt=&quot;抛出异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而作为 API，验证路径的合法性也是需要抛出异常的，所以大可以继续使用这样的方法，用方法内部抛出的异常来提醒开发者传入的路径不合法。&lt;/p&gt;

&lt;p&gt;但有时候是作为与用户的交互来判断路径或者文件名是否合法的，那么这个时候使用异常就不太合适了。毕竟 C#/.NET 的异常机制不应该参与正常的逻辑流程。&lt;/p&gt;

&lt;p&gt;那么可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path.GetInvalidFileNameChars()&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetInvalidPathChars()&lt;/code&gt; 来判断字符串中是否包含不合法的文件名字符或者路径字符。&lt;/p&gt;

&lt;p&gt;以下代码来自 .NET Core 的库源码 &lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/Path.Windows.cs&quot;&gt;Path.Windows.cs&lt;/a&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetInvalidFileNameChars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sc&quot;&gt;'\&quot;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'&amp;lt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'&amp;gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'|'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'\0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;':'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'?'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'\\'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'/'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetInvalidPathChars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sc&quot;&gt;'|'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'\0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3067479/determine-via-c-sharp-whether-a-string-is-a-valid-file-path&quot;&gt;.net - Determine via C# whether a string is a valid file path - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/62771/how-do-i-check-if-a-given-string-is-a-legal-valid-file-name-under-windows&quot;&gt;c# - How do I check if a given string is a legal/valid file name under Windows? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://codereview.stackexchange.com/questions/120002/windows-filepath-and-filename-validation&quot;&gt;c# - Windows filepath and filename validation - Code Review Stack Exchange&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/Path.Windows.cs&quot;&gt;Path.Windows.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs&quot;&gt;PathHelper.Windows.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs&quot;&gt;PathInternal.Windows.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 16 Jan 2019 12:18:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/verify-a-string-as-a-file-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/verify-a-string-as-a-file-path.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>让 MSBuild Target 支持 Clean</title>
        <description>&lt;p&gt;我们有时候会使用解决方案的清理（Clean）功能来解决一些项目编译过程中非常诡异的问题。这通常是一些 Target 生成了一些错误的中间文件，但又不知道到底是哪里错了。&lt;/p&gt;

&lt;p&gt;我们自己编写 Target 的时候，也可能会遇到这样的问题，所以让我们自己的 Target 也能支持 Clean 可以在遇到诡异问题的时候，用户可以自己通过清理解决方案来消除错误。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;以下代码来自于 &lt;a href=&quot;https://github.com/dotnet-campus/SourceFusion/blob/master/src/SourceFusion.Tool/Assets/build/Package.targets&quot;&gt;SourceFusion/Package.targets&lt;/a&gt;。这是我主导开发的一个预编译框架，用于在编译期间执行各种代码，以便优化代码的运行期性能。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CleanDependsOn&amp;gt;&lt;/span&gt;$(CleanDependsOn);_SourceFusionClean&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CleanDependsOn&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;&amp;lt;!--清理 SourceFusion 计算所得的文件--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_SourceFusionClean&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;_DefaultSourceFusionWorkingFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(_DefaultSourceFusionWorkingFolder)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;obj\$(Configuration)\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/_DefaultSourceFusionWorkingFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionWorkingFolder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(SourceFusionWorkingFolder)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(_DefaultSourceFusionWorkingFolder)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionWorkingFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionToolsFolder&amp;gt;&lt;/span&gt;$(SourceFusionWorkingFolder)SourceFusion.Tools\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionToolsFolder&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SourceFusionGeneratedCodeFolder&amp;gt;&lt;/span&gt;$(SourceFusionWorkingFolder)SourceFusion.GeneratedCodes\&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionGeneratedCodeFolder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RemoveDir&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directories=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(SourceFusionToolsFolder);$(SourceFusionGeneratedCodeFolder)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的作用便是支持 Visual Studio 中的解决方案清理功能。通过指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;CleanDependsOn&lt;/code&gt; 属性的值给一个新的 Target，使得在 Clean 的时候，这个 Target 能够执行。我在 Target 中删除了我生成的所有中间文件。&lt;/p&gt;

&lt;p&gt;你可以通过阅读 &lt;a href=&quot;/post/extend-the-visual-studio-build-process&quot;&gt;通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程&lt;/a&gt; 来了解这个 Target 是如何工作起来的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-clean-a-build?view=vs-2017&quot;&gt;How to: Clean a Build - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 16 Jan 2019 07:20:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/support-clean-for-msbuild-target.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/support-clean-for-msbuild-target.html</guid>
        
        
        <category>msbuild</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>出于迁移项目的考虑，GitHub 中 Fork 出来的项目，如何与原项目断开 Fork 关系？</title>
        <description>&lt;p&gt;如果需要为 GitHub 上的项目做贡献，我们通常会 Fork 到自己的名称空间下。在推送代码之后添加 pull request 时，GitHub 会自动为我们跨仓库建立 pull request 的连接，非常方便。但是，如果 Fork 是出于项目的迁移，例如从个人名下迁移到某个组织下或者反过来，那么这种自动的 pull request 的设置就很影响效率了。&lt;/p&gt;

&lt;p&gt;那么这种情况如何处理呢？如何断开 Fork 连接呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 GitHub 的官方帮助页面 &lt;a href=&quot;https://help.github.com/articles/why-are-my-contributions-not-showing-up-on-my-profile/#commit-was-made-in-a-fork&quot;&gt;Commit was made in a fork&lt;/a&gt; 中，有这一段话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;To detach the fork and turn it into a standalone repository on GitHub, contact &lt;a href=&quot;https://github.com/contact&quot;&gt;GitHub Support&lt;/a&gt; or &lt;a href=&quot;https://premium.githubsupport.com/&quot;&gt;GitHub Premium Support&lt;/a&gt;. If the fork has forks of its own, let support know if the forks should move with your repository into a new network or remain in the current network. For more information, see “&lt;a href=&quot;https://help.github.com/articles/about-forks/&quot;&gt;About forks&lt;/a&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，你是不能通过自己的操作来断开 Fork 联系的。这是当然的，毕竟随意就能断开的话，开源的一方就非常容易失去对源码的控制权，这很不利于开源社区的贡献。&lt;/p&gt;

&lt;p&gt;你需要做的，是进入 GitHub 支持页面 &lt;a href=&quot;https://github.com/contact&quot;&gt;https://github.com/contact&lt;/a&gt; 在里面填写你的请求，要求 GitHub 官方支持人员手动断开 Fork 关联。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-25-09-50-53.png&quot; alt=&quot;填写请求&quot; /&gt;&lt;/p&gt;

&lt;p&gt;填写完之后，等待 GitHub 官方人员处理：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-25-09-51-27.png&quot; alt=&quot;保持联系&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当 GitHub 官方人员处理完之后，会给出回复邮件，告知 Fork 关系已经反转：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-15-19-50-26.png&quot; alt=&quot;主仓库已经改变&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/16052845/6233938&quot;&gt;Delete fork dependency of a GitHub repository - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://help.github.com/articles/why-are-my-contributions-not-showing-up-on-my-profile/#commit-was-made-in-a-fork&quot;&gt;Why are my contributions not showing up on my profile? - User Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/contact&quot;&gt;Contact GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 15 Jan 2019 11:50:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-detach-the-fork-in-github.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-detach-the-fork-in-github.html</guid>
        
        
        <category>github</category>
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET 中 GetHashCode 的哈希值有多大概率会相同（哈希碰撞）</title>
        <description>&lt;p&gt;如果你试图通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; 得到的一个哈希值来避免冲突，你可能要失望了。因为实际上 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; 得到的只是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt; 的结果，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt; 只有 32 个 bit。&lt;/p&gt;

&lt;p&gt;32 个 bit 的哈希，有多大概率是相同的呢？本文将计算其概率值。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; 得到的哈希值，&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;9292 个对象的哈希值冲突概率为 1%；&lt;/li&gt;
  &lt;li&gt;77163 个对象的哈希值冲突概率为 50%。&lt;/li&gt;
&lt;/ol&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;计算方法&quot;&gt;计算方法&lt;/h2&gt;

&lt;p&gt;计算哈希碰撞概率的问题可以简化为这样：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有 1, 2, 3, … \(n\) 这些数字；&lt;/li&gt;
  &lt;li&gt;现在，随机从这些数字中取出 \(k\) 个；&lt;/li&gt;
  &lt;li&gt;计算这 \(k\) 个数字里面出现重复数字的概率。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有 1, 2, 3, 4 这四个不同的数字；&lt;/li&gt;
  &lt;li&gt;现在从中随机抽取 2 个。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;那么抽取出来的可能的情况总数为：&lt;/p&gt;

\[4^2\]

&lt;p&gt;一定不会重复的可能的情况总数为：&lt;/p&gt;

\[4\times3\]

&lt;p&gt;意思是，第一次抽取的时候有 4 个数字可以选，而第二次抽取的时候就只有 3 个数字可以选了。&lt;/p&gt;

&lt;p&gt;那么，会出现重复的概率就是：&lt;/p&gt;

\[1-\frac{4\times3}{4^2}\]

&lt;p&gt;也就是 25% 的概率会出现重复。&lt;/p&gt;

&lt;p&gt;那么现在，我们随机抽取 3 个会怎样呢？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有 1, 2, 3, 4 这四个不同的数字；&lt;/li&gt;
  &lt;li&gt;现在从中随机抽取 3 个。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;那么，会出现重复的概率就是：&lt;/p&gt;

\[1-\frac{4\times3\times2}{4^3}\]

&lt;p&gt;也就是 37.5%，64 种可能里面，有 24 种是有重复的。&lt;/p&gt;

&lt;p&gt;现在，我们推及到 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; 函数的重复情况。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; 实际上返回的是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt; 值，占 32 bit。也就是说，我们有 \(2^{32}\) 个数字可以选。&lt;/p&gt;

&lt;p&gt;现在问题是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;有 1, 2, 3, … \(2^{32}\) 这些数字，我们把 \(2^{32}\) 记为 \(n\)；&lt;/li&gt;
  &lt;li&gt;现在从中随机抽取 \(k\) 个。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;那么会出现重复的概率为：&lt;/p&gt;

\[1-\frac{n\times(n-1)\times(n-2)\times...(n-k+1)}{n^k}\]

&lt;p&gt;当然，分子分母都有的 \(n\) 可以约去：&lt;/p&gt;

\[1-\frac{(n-1)\times(n-2)\times...(n-k+1)}{n^{k-1}}\]

&lt;h2 id=&quot;计算的简化&quot;&gt;计算的简化&lt;/h2&gt;

&lt;p&gt;而 \(k\) 很大的时候，此概率的计算非常复杂。然而我们可以取近似值简化成如下形式 &lt;a href=&quot;https://preshing.com/20110504/hash-collision-probabilities/&quot;&gt;[1]&lt;/a&gt;：&lt;/p&gt;

\[1-e^{\frac{-k(k-1)}{2n}}\]

&lt;p&gt;当然，实际上此计算在 \(k\) 取值较小的时候还可以进一步简化成：&lt;/p&gt;

\[\frac{k(k-1)}{2n}\]

&lt;p&gt;于是，在日常估算的时候，你甚至可以使用计算器估算出哈希值碰撞的概率。&lt;/p&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;https://preshing.com/20110504/hash-collision-probabilities/&quot;&gt;Hash Collision Probabilities&lt;/a&gt; 了解更多关于计算简化的内容。&lt;/p&gt;

&lt;h2 id=&quot;概率图&quot;&gt;概率图&lt;/h2&gt;

&lt;p&gt;为了直观感受到 32 bit 的哈希值的碰撞概率与对象数量之间的关系，我从 &lt;a href=&quot;https://blogs.msdn.microsoft.com/ericlippert/2010/03/22/socks-birthdays-and-hash-collisions/&quot;&gt;Socks, birthdays and hash collisions&lt;/a&gt; 和 &lt;a href=&quot;https://preshing.com/20110504/hash-collision-probabilities/&quot;&gt;Hash Collision Probabilities&lt;/a&gt; 找到了计算好的概率数据，并绘制成一张图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-08-14-51-31.png&quot; alt=&quot;32 bit 的哈希值碰撞概率图&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/7969189/6233938&quot;&gt;c# - Probability of getting a duplicate value when calling GetHashCode() on strings - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/ericlippert/2010/03/22/socks-birthdays-and-hash-collisions/&quot;&gt;Socks, birthdays and hash collisions – Fabulous Adventures In Coding&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://preshing.com/20110504/hash-collision-probabilities/&quot;&gt;Hash Collision Probabilities&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Thu, 10 Jan 2019 03:41:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hash-collisions-of-gethashcode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hash-collisions-of-gethashcode.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>int? 竟然真的可以是 null！.NET/C# 确定可空值类型 Nullable&lt;T&gt; 实例的真实类型</title>
        <description>&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 我们可以为原本不可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的值类型像引用类型那样提供一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 值。不过注意：&lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 本身也是个 &lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt;，是个值类型哦。这意味着你随时可以调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.HasValue&lt;/code&gt; 这样的方法，而不用担心会出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;等等！除了本文提到的一些情况。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;nullable-中的-null&quot;&gt;Nullable&lt;T&gt; 中的 null&lt;/T&gt;&lt;/h2&gt;

&lt;p&gt;注意看以下的代码。我们创建了一个值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;int?&lt;/code&gt;，然后依次输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 的值、&lt;code class=&quot;highlighter-rouge&quot;&gt;value.GetType()&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你觉得可以得到什么结果呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;value = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;type  = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;TYPE  = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;结果是……&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;果是……&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;是……&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;……&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;崩掉了……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-19-32-19.png&quot; alt=&quot;NullReferenceException&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 后面加个空传递运算符：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  Console.WriteLine($&quot;type  = {value.GetType()}&quot;);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  Console.WriteLine($&quot;type  = {value?.GetType()}&quot;);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在再次运行，我们确认了 &lt;code class=&quot;highlighter-rouge&quot;&gt;value?.GetType()&lt;/code&gt; 的值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(int?)&lt;/code&gt; 的类型为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;Int32&amp;gt;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-19-36-36.png&quot; alt=&quot;null 的类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，我们现在将 &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; 的值从 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--  int? value = GetValue(null);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++  int? value = GetValue(1);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;竟然 &lt;code class=&quot;highlighter-rouge&quot;&gt;value.GetType()&lt;/code&gt; 得到的类型是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-19-38-00.png&quot; alt=&quot;1 的类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是我们可以得出结论：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对于可空值类型，当为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 会出现空引用异常；&lt;/li&gt;
  &lt;li&gt;对于可空值类型，当不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 返回的是对应的基础类型，而不是可空值类型；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(int?)&lt;/code&gt; 能够得到可空值类型。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;objectgettype-和-is-对-nullable-的作用&quot;&gt;Object.GetType() 和 is 对 Nullable&lt;T&gt; 的作用&lt;/T&gt;&lt;/h2&gt;

&lt;p&gt;在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type&quot;&gt;docs.microsoft.com&lt;/a&gt; 中，有一段对此的描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When you call the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.object.gettype&quot;&gt;Object.GetType&lt;/a&gt; method on an instance of a nullable type, the instance is &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/using-nullable-types#boxing-and-unboxing&quot;&gt;boxed&lt;/a&gt; to &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.object&quot;&gt;Object&lt;/a&gt;. As boxing of a non-null instance of a nullable type is equivalent to boxing of a value of the underlying type, &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.object.gettype&quot;&gt;GetType&lt;/a&gt; returns a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.type&quot;&gt;Type&lt;/a&gt; object that represents the underlying type of a nullable type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意思是说，当你对一个可空值类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.GetType()&lt;/code&gt; 方法的时候，这个实例会被装箱，会被隐式转换为一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 对象。然而对可空值类型的装箱与对值类型本身的装箱是同样的操作，所以调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 的时候都是返回这个对象对应的实际基础类型。例如对一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;int?&lt;/code&gt; 进行装箱和对 &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; 装箱得到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 对象是一样的，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 实际上是不能区分这两种情况的。&lt;/p&gt;

&lt;p&gt;那什么样的装箱会使得两个不同的类型被装箱为同一个了呢？&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/using-nullable-types&quot;&gt;另一篇文档&lt;/a&gt;描述了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 装箱的过程：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;If HasValue returns false, the null reference is produced.&lt;/li&gt;
    &lt;li&gt;If HasValue returns true, a value of the underlying value type T is boxed, not the instance of Nullable&lt;T&gt;.&lt;/T&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;HasValue&lt;/code&gt; 返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，那么就装箱一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;HasValue&lt;/code&gt; 返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么就将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 进行装箱，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 的实例。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这才是为什么 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 会得到以上结果的原因。&lt;/p&gt;

&lt;p&gt;同样的，也不能使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt; 运算符来确定这个类型到底是不是可空值类型：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;value is int  = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;value is int? = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终得到两者都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-20-14-28.png&quot; alt=&quot;用 is 确定类型&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;应该如何判断可空值类型的真实类型&quot;&gt;应该如何判断可空值类型的真实类型&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable.GetUnderlyingType(type)&lt;/code&gt; 方法，能够得到一个可空值类型中的基础类型，也就是得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 的类型。如果得不到就返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以使用以下方法可以判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 的真实类型。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsNullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetUnderlyingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 的实例怎么来呢？根据前面的示例代码，我们又不能调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetType()&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;实际上，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 的实例就是拿不到，在运行时是不能确定的。我们只能在编译时确定，就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsOfNullableType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetUnderlyingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你是运行时拿到的可空值类型的实例，那么实际上此方法也是无能为力的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv's demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;value is nullable? &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsOfNullableType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;o     is nullable? &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsOfNullableType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsOfNullableType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetUnderlyingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-20-27-41.png&quot; alt=&quot;运行时是拿不到的&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/785358/6233938&quot;&gt;c# - Nullable type is not a nullable type? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type&quot;&gt;How to: Identify a nullable type - C# Programming Guide - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/using-nullable-types&quot;&gt;Using nullable types - C# Programming Guide - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 08 Jan 2019 10:01:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-identify-a-nullable-type.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-identify-a-nullable-type.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>签署 Centennial Program Addendum，使用 Desktop Bridge 将 Win32 应用转制成 UWP</title>
        <description>&lt;p&gt;能上架 Windows 应用商店的并不一定必须是 UWP 应用程序或者 PWA 程序，也可以是普通的 Win32 应用程序。典型的上架应用商店的应用有微信、Telegram、Snipaste 等。使用 Desktop Bridge，我们即可以为我们的普通 Win32 应用程序做一个 UWP 的包来。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;商店中那些转制的应用&quot;&gt;商店中那些转制的应用&lt;/h2&gt;

&lt;p&gt;如果你并没有感受到 Win32 转制的商店应用和原生的 UWP 或 PWA 应用有什么不同，可以尝试体验下面的三款转制应用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;微信 For Windows&lt;/strong&gt; &lt;a href=&quot;https://www.microsoft.com/store/productId/9NBLGGH4SLX7&quot;&gt;https://www.microsoft.com/store/productId/9NBLGGH4SLX7&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Telegram Desktop&lt;/strong&gt; &lt;a href=&quot;https://www.microsoft.com/store/productId/9NZTWSQNTD0S&quot;&gt;https://www.microsoft.com/store/productId/9NZTWSQNTD0S&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snipaste&lt;/strong&gt; &lt;a href=&quot;https://www.microsoft.com/store/productId/9P1WXPKB68KX&quot;&gt;https://www.microsoft.com/store/productId/9P1WXPKB68KX&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;了解-desktop-bridge&quot;&gt;了解 Desktop Bridge&lt;/h2&gt;

&lt;p&gt;Desktop Bridge，可能还可以叫做“桌面桥”，它存在的目的便是将已有的 WPF 程序、Windows Forms 程序和其他 Win32 应用转换成应用商店应用。而桌面桥提供了一种与 UWP 一致的 Windows 应用包，使用这种 Windows 应用包，普通的 Win32 应用也能访问 UWP 的 API。&lt;/p&gt;

&lt;p&gt;需要注意的是，Desktop Bridge 要求的 Windows 系统最低版本为 1607。也就是说，如果要选择 SDK 的版本，需要选择 10.0.14393 或以上版本。&lt;/p&gt;

&lt;p&gt;当然，并不是所有的 Win32 应用程序都支持直接转制到 UWP，如果应用会动态加载不在安装包中的 dll 或者会试图修改系统文件和配置，那么必须去掉这些代码才能完成转制。如果希望了解更多不支持的类型，建议阅读官方文档：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-prepare?wt.mc_id=MVP&quot;&gt;Prepare to package an app (Desktop Bridge) - UWP app developer - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;!-- ### 转制前的准备

我们需要提前下载好转制工具和 SDK：

- [Desktop App Converter](https://aka.ms/converter)
- [Desktop App Converter 基础系统镜像](https://aka.ms/converterimages)

Desktop App Converter 会从应用商店安装，安装好后可以在开始菜单中找到。不用着急去运行它，因为我们稍后会通过 Powershell 并以管理员权限运行。

![](/static/posts/2018-06-23-19-43-16.png)

Desktop App Converter 基础系统镜像下载完后随便放到某个地方，稍后我们能够通过命令行找到即可。

待一切下载完毕，我们以管理员权限运行 PowerShell，然后开始运行命令：

```powershell
&gt; Set-ExecutionPolicy bypass
```

如果中途提示脚本的安全性问题，选择 `[Y] Yes` 或 `[A] Yes to All` 都是可以的。

紧接着运行 DesktopAppConverter.exe 并将 `.\BaseImage-1XXXX.wim` 部分改成前面下载的基础系统镜像的路径。

```powershell
&gt; DesktopAppConverter.exe -Setup -BaseImage .\BaseImage-1XXXX.wim -Verbose
```

如果提示需要重启计算机，那么重启即可。 --&gt;

&lt;h2 id=&quot;在-visual-studio-中创建-windows-应用打包工程&quot;&gt;在 Visual Studio 中创建 Windows 应用打包工程&lt;/h2&gt;

&lt;p&gt;使用 Visual Studio 打开原来的 Win32 程序的解决方案，在解决方案中新建一个 Windows 应用程序包项目（Windows Application Packaging Project）。我们将使用这个项目为转制应用打包。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-23-19-58-42.png&quot; alt=&quot;Windows Application Packaging Project&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在选择 SDK 时，目标版本我选择了 17134，但注意最低版本必须是 14393 或以上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-10-04-34.png&quot; alt=&quot;选择 SDK 版本&quot; /&gt;&lt;/p&gt;

&lt;p&gt;稍等片刻，我们便能看到 Visual Studio 已经为我们准备好的应用程序包工程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-10-07-51.png&quot; alt=&quot;Whitman.Package 工程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Applications（应用程序）一栏我们右击选择添加引用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-23-20-43-51.png&quot; alt=&quot;添加引用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-10-09-49.png&quot; alt=&quot;选择我们此前的程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后展开 Applications（应用程序）一栏，将我们的 Win32 程序右击设为入口点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-10-10-40.png&quot; alt=&quot;设置入口点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编译刚刚设置好的打包项目。如果之前的项目能够编译通过，那么这个新的打包项目理论上也是能编译通过的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-10-37-43.png&quot; alt=&quot;调试部署的 Whitman&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将这个新项目设置为启动项目，启动它即可进行正常的调试，能够正常断点、单步等等。&lt;/p&gt;

&lt;h2 id=&quot;修改包清单并发布应用&quot;&gt;修改包清单并发布应用&lt;/h2&gt;

&lt;h3 id=&quot;各种元数据&quot;&gt;各种元数据&lt;/h3&gt;

&lt;p&gt;另外，转制的应用和原生的 UWP 应用一样，发布之前也需要为应用设计图标，设置应用显示名称、包名称、关联应用商店。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-13-42-36.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不得不说，为商店应用设计图标是一件非常繁杂的工作，不过，最终的效果确实非常喜人的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-13-41-05.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;需要注意，在 &lt;a href=&quot;https://dev.windows.com&quot;&gt;https://dev.windows.com&lt;/a&gt; 上发布应用时，由于我们是转制的应用，所以 runFullTrust 是必选项。如果你在提交应用时遇到了以下提示框，微软的官方文档提示无需写明理由。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This capability is also required for any desktop application that is delivered as an appx package (as with the Desktop Bridge), and it will automatically appear in your manifest when packaging these apps using the Desktop App Converter (DAC) or Visual Studio. You won’t need to request approval to use this capability if you already received permission using our form.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-24-14-36-50.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;建议在给审核人员的提示中，写明我们是转制应用，以引起审核人员的注意。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-07-04-03.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;通过认证&quot;&gt;通过认证&lt;/h3&gt;

&lt;p&gt;如果没有接受 Centennial Program Addendum，那么提交是不被允许的，并且在上传 appxupload 的时候会有警告开始提示了：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Package acceptance validation warning: You must contact &lt;a href=&quot;mailto:partnerops@microsoft.com&quot;&gt;partnerops@microsoft.com&lt;/a&gt; and get approval before you can submit this app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当然，你可以忽略这样的警告继续提交，但那样的话最终认证会失败，并提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Notes To Developer&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Your developer account has not been approved to submit apps converted with the Desktop Bridge as you have not yet accepted the Centennial Program Addendum. Please resubmit your request for approval.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果无视警告，那么下次提交提示就会变成错误而不是警告了：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Package acceptance validation error: You need to accept the &lt;a href=&quot;https://go.microsoft.com/fwlink/?linkid=873135&quot;&gt;Centennial Program Addendum&lt;/a&gt; before you can submit this app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-12-16-26-57.png&quot; alt=&quot;Centennial Program Addendum&quot; /&gt;&lt;br /&gt;
▲ You need to accept the Centennial Program Addendum before you can submit this app.&lt;/p&gt;

&lt;p&gt;提示要求我们必须同意 Centennial Program Addendum 协议，然而我们在 &lt;a href=&quot;https://partner.microsoft.com/en-us/dashboard/windows/overview&quot;&gt;https://partner.microsoft.com/en-us/dashboard/windows/overview&lt;/a&gt; 的设置中点进去 Agreements 是找不到这项协议的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-06-59-21.png&quot; alt=&quot;Agreements&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-07-01-01.png&quot; alt=&quot;已签署协议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;以下划重点&lt;/strong&gt;！！！&lt;/p&gt;

&lt;p&gt;你需要先提交应用，并时常关注 &lt;a href=&quot;https://partner.microsoft.com/en-us/dashboard/account/agreements&quot;&gt;https://partner.microsoft.com/en-us/dashboard/account/agreements&lt;/a&gt; 中是否会新增一项协议提示，就是下图这个：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-06-58-13.png&quot; alt=&quot;Centennial Program Addendum 协议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当出现时，点击接受即可。&lt;strong&gt;这项协议在微软商店发给你的三天之内才会出现，超过三天还没有同意，这项协议就会自动移除&lt;/strong&gt;。在我的实际提交中，等待邮件通知之时，三天基本上都过完了，所以稍微不及时收邮件，这项协议就点不开了，就只能看到下面这张图片感叹一声 —— 又要重来！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-13-08-32-31.png&quot; alt=&quot;Page Not Found&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在同意了协议之后，你的协议列表中就会额外出现 Centennial Program Addendum 协议了。以后你可以继续提交转制应用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-07-08-31.png&quot; alt=&quot;签署的开发者协议&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-root?wt.mc_id=MVP&quot;&gt;Desktop Bridge - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations?wt.mc_id=MVP&quot;&gt;App capability declarations - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/appconsult/2018/02/20/package-acceptance-validation-error-when-you-submit-a-uwp-desktop-bridge-app-on-the-store/&quot;&gt;“Package acceptance validation error” when you submit a UWP + Desktop Bridge app on the Store – App Consult Team&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/appconsult/2017/09/25/preparing-a-desktop-bridge-application-for-the-store-submission/&quot;&gt;Preparing a Desktop Bridge application for the Store submission – App Consult Team&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-distribute?wt.mc_id=MVP&quot;&gt;Publish your packaged desktop app to a Windows store or sideload it onto one or more devices. - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 08 Jan 2019 10:01:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows-desktop-bridge.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows-desktop-bridge.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>C# 中委托实例的命名规则</title>
        <description>&lt;p&gt;我们知道一个类中的属性应该用名词或名词性短语，方法用动词或动宾短语；但是委托的实例却似乎有一些游离。因为在 .NET 中委托代表的是一个动作，既可以把它看作是名词，也可以看作是动词。在用法上，既可以像属性和变量一样被各种传递，也可以像一个方法一样被调用。&lt;/p&gt;

&lt;p&gt;那么委托实例的命名，应该遵循属性和变量的命名，还是遵循方法的命名呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;委托的实例可以当作属性或者变量使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;委托的实例也可以当作方法使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv is a 逗比&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是委托的命名方式迁就名词还是动词呢？&lt;/p&gt;

&lt;p&gt;在微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines&quot;&gt;Naming Guidelines&lt;/a&gt; 中提到了 .NET 中约定的命名方式。对于委托的命名，实际上只在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members#names-of-events&quot;&gt;Names of Type Members&lt;/a&gt; 中提到了，不过提及的实际上是事件型的委托，而不是一般的委托实例。然后，微软其他地方的官方文档中也没有单独提及委托的命名方式。&lt;/p&gt;

&lt;p&gt;为了弄清楚第一方代码的命名规则，我去 &lt;a href=&quot;https://source.dot.net/&quot;&gt;https://source.dot.net/&lt;/a&gt; 上找了一些使用了委托的代码，然后发现，对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Func&lt;/code&gt; 系列委托的命名，有以下这些（部分名称只保留了后缀进行合并）：&lt;/p&gt;

&lt;p&gt;使用名词的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;action&lt;/li&gt;
  &lt;li&gt;function&lt;/li&gt;
  &lt;li&gt;callback&lt;/li&gt;
  &lt;li&gt;continuation&lt;/li&gt;
  &lt;li&gt;method&lt;/li&gt;
  &lt;li&gt;factory&lt;/li&gt;
  &lt;li&gt;valueFactory&lt;/li&gt;
  &lt;li&gt;creator&lt;/li&gt;
  &lt;li&gt;valueGetter&lt;/li&gt;
  &lt;li&gt;initializer&lt;/li&gt;
  &lt;li&gt;_target&lt;/li&gt;
  &lt;li&gt;attributeComputer&lt;/li&gt;
  &lt;li&gt;argumentsPromise&lt;/li&gt;
  &lt;li&gt;taskProvider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用动词的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;getSource&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用缩略词的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;localInit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我把缩略词单独拿出来，是因为缩写了以下就看不出来这到底是缩自名词还是缩自动词。&lt;/p&gt;

&lt;p&gt;基本上可以确定：&lt;/p&gt;

&lt;p&gt;委托实例的命名是 —— 一个表示动作的&lt;strong&gt;名词&lt;/strong&gt;！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/&quot;&gt;Source Browser&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 08 Jan 2019 09:09:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/name-convention-of-delegate-instance.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/name-convention-of-delegate-instance.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>三值 bool? 进行与或运算后的结果</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bool?&lt;/code&gt; 实际上是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;Boolean&amp;gt;&lt;/code&gt; 类型，可以当作三值的 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool&lt;/code&gt; 类型来使用。不过三值的布尔进行与或运算时的结果与二值有什么不同吗？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重载条件逻辑运算符与或&quot;&gt;重载条件逻辑运算符“与”（&amp;amp;&amp;amp;）“或”（||）&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;在 [C# 重载条件逻辑运算符（&amp;amp;&amp;amp; 和&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;）](/post/overload-conditional-and-and-or-operators-in-csharp) 一文中我说明了如何重载条件逻辑运算符 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt;。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这两个运算符不能直接重载，但可以通过重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; 运算符来间接完成。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool?&lt;/code&gt;，重载了这样两个运算符：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bool? operator &amp;amp;(bool? x, bool? y)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bool? operator |(bool? x, bool? y)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是我们可以得到三值 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool?&lt;/code&gt; 的与或结果。&lt;/p&gt;

&lt;h2 id=&quot;三值-bool-的与或结果&quot;&gt;三值 bool? 的与或结果&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x&amp;amp;y&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x|y&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/using-nullable-types&quot;&gt;Using nullable types - C# Programming Guide - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 06 Jan 2019 12:52:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-semantics-of-three-value-bool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-semantics-of-three-value-bool.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>为什么我们不应该使用微信或者 QQ 作为团队协作的 IM 工具？</title>
        <description>&lt;p&gt;如果你的团队没有觉得微信是低效的团队 IM 工具，那只有两种可能：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;团队成员很少使用微信进行私人的生活和娱乐。&lt;/li&gt;
  &lt;li&gt;你就是一个低效的团队，而且还不自知。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;微信连接一切&quot;&gt;微信，连接一切&lt;/h2&gt;

&lt;p&gt;微信，&lt;a href=&quot;http://media.people.com.cn/n/2014/1231/c40606-26304535.html&quot;&gt;连接一切&lt;/a&gt;。除了家人、朋友、同学这些熟人关系，还有同事、客户、用户、企业号这些工作上的关系，还有各种各种办事小程序、各种资讯公众号、各种商店服务号、快递通知等生活和私人的快捷入口。&lt;/p&gt;

&lt;h2 id=&quot;每个人都有微信&quot;&gt;每个人都有微信&lt;/h2&gt;

&lt;p&gt;微信的一大目标是让所有人都能用，所以其 UX 体验必须照顾所有人——要尽一切可能简单。这里我不需要说张小龙是如何做到这一点的，但是他就是做到了。因为即便是家里常年不碰手机的老人们都能够使用微信发一些语音消息，抢一些红包，秀一秀朋友圈了。&lt;/p&gt;

&lt;p&gt;曾经见面要电话联系方式，因为知道每个人都有电话，一定能要得到的。而现在见面会要微信，因为确认每个人都有微信的。所以使用微信来做任何事情的习惯会不断在人群中传播。在某个线下活动中见了一面加一个微信和一堆微信群，在某个工作事件中加一堆内部和外部的微信群。反正，所有人的所有活动都用微信连接着你。&lt;/p&gt;

&lt;h2 id=&quot;微信低成本的沟通方式&quot;&gt;微信，低成本的沟通方式&lt;/h2&gt;

&lt;p&gt;发短信不是一个能确保送达的方式，因为现在的短信功能基本上只有广告和验证码的功能，没有人会认真去查阅短信收件箱的。所以短信的成本从原来的套餐价格成本提升到了别人可能不看的成本。&lt;/p&gt;

&lt;p&gt;为了能够打一个电话，你得差不多确认对方应该有空才行，自己也需要一个可以大声说话的场合。而且打过去对方不一定方便接听。这就是较高的接听成本。&lt;/p&gt;

&lt;p&gt;邮件，就像写信一样。我们很重视写信，会尽可能说明要点，写明事情。于是写邮件这个过程就是一个高成本的过程。&lt;/p&gt;

&lt;p&gt;而微信，你无需进行过多的思考，只要你想，打几个字就能发出去。而且你还不用担心对方是否有空，因为当他有空的时候也一定会看见。所以发送的成本低，确保别人收到的成本也低。所以所有种类的人都可以有事没事在微信里面找你一下。&lt;/p&gt;

&lt;h2 id=&quot;不间断的交叉消息干扰&quot;&gt;不间断的交叉消息干扰&lt;/h2&gt;

&lt;p&gt;咚！技术大佬发话了……&lt;/p&gt;

&lt;p&gt;咚！快递到了……&lt;/p&gt;

&lt;p&gt;咚！新的缴费通知……&lt;/p&gt;

&lt;p&gt;咚！你有一些新的资讯……&lt;/p&gt;

&lt;p&gt;咚！最新的客户问题跟进如何了……&lt;/p&gt;

&lt;p&gt;咚！今晚要不要出去聚餐……&lt;/p&gt;

&lt;p&gt;每时每刻，微信不断有消息袭来。早上、上午、中午、下午、晚上、深夜……&lt;/p&gt;

&lt;p&gt;一个眨眼的功夫，微信图标上就会多几个数字。&lt;/p&gt;

&lt;p&gt;我们将一个群设置为免打扰，是因为这个群会说到一些有用的信息，只是有效信息密度太低。虽然可以设置群消息免打扰，但不能屏蔽。免打扰的群消息依然占据一个聊天会话的位置。甚至我想找某人聊一些事情的时候，前一秒正准备点击那个会话，后一秒就被其他群的新消息置顶改变了会话顺序，于是会点错。&lt;/p&gt;

&lt;p&gt;知识工作者思考打断的恢复成本很高。在工作期间会收到各种社区、朋友和第三方服务等非工作消息，在非工作期间会收到各种工作类的消息。这种交叉干扰使得我们做任何一件事情都比以前更难专注。如果不去看消息，又不能知道哪些消息是重要紧急的，哪些消息是自己关心的，哪些消息是无关紧要可以随后再看的。&lt;/p&gt;

&lt;p&gt;于是，这种手工的消息分类和过滤，会发生在一天的每一个时刻，深入到工作和生活的每一个角落。&lt;/p&gt;

&lt;h2 id=&quot;不止是消息的交叉干扰&quot;&gt;不止是消息的交叉干扰&lt;/h2&gt;

&lt;p&gt;以上只是痛点问题，而实际上还有其他的效率问题：&lt;/p&gt;

&lt;p&gt;微信是为移动端而优化的应用。而实际上一旦说到效率，PC 端是远胜于移动端的。PC 端的信息承载量大、而键盘和鼠标能够快速处理大量信息；这一点是单页的移动端应用和触摸操作远不能比的。&lt;/p&gt;

&lt;p&gt;然而，微信的 PC 端每次登录需要使用手机扫码或者点击，每天一次就意味着不断在无效的操作上浪费时间。&lt;/p&gt;

&lt;p&gt;另外，微信的移动端和 PC 端是不自动互通的，具体来说是消息不互通，如果先在移动端收到了一份文件，还需要折腾一阵才能在 PC 端打开使用。&lt;/p&gt;

&lt;h2 id=&quot;别随便什么事儿都拉一个群&quot;&gt;别随便什么事儿都拉一个群&lt;/h2&gt;

&lt;p&gt;当然，我指的是工作上的事情。因为多数人更关心工作上的效率，而不是娱乐上的效率。&lt;/p&gt;

&lt;p&gt;微信上的消息那么多，如果这件事情与对方的关系不大，你根本不能指望对方能够时刻关注着群里事件的最新动向。嗯，这个群在对方的微信里面，只是众多垃圾信息中的一个而已。&lt;/p&gt;

&lt;p&gt;IM 不适合用来沟通事情的进展，只是用来沟通。所以经常总结进展，仅在必要某人的时候将某人加入，阅读总结的进展才是高效的。&lt;/p&gt;

&lt;h2 id=&quot;好一些的-qqtim&quot;&gt;好一些的 QQ/TIM&lt;/h2&gt;

&lt;p&gt;TIM 不像微信那样期望所有人用，所以可以在里面放上更多高级的功能。比如群的消息屏蔽，可以设置为完全屏蔽到一个群助手里面。比如群管理，可以有更多的管理方式。&lt;/p&gt;

&lt;p&gt;使用 TIM 至少可以根据群的有效信息密度划分更多等级的提示级别，可以比微信更容易的筛选和过滤对自己有用的消息。&lt;/p&gt;

&lt;p&gt;另外，QQ/TIM 的移动端和 PC 端的同步是实时的自动的。可以随时在 PC 端继续，而无需受限在移动端上。&lt;/p&gt;

&lt;p&gt;不过，QQ/TIM 依然聚合了各种维度的信息，依然会存在各种信息的交叉干扰。&lt;/p&gt;

&lt;h2 id=&quot;我们需要新的消息过滤和聚合方式&quot;&gt;我们需要新的消息过滤和聚合方式&lt;/h2&gt;

&lt;p&gt;实际上，只要团队协作不使用 QQ 或者微信这种聚合所有交流的工具进行团队协作，就能够大大降低团队成员的信息过滤的成本。&lt;/p&gt;

&lt;p&gt;在工作期间，只注重团队专用 IM 工具发来的消息，而对于 QQ/微信发来的消息可以延迟统一查看。如果这款工具做得更好，那么可以为团队 IM 的不同消息进行分级：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;紧急的工作任务，应该只在紧急情况使用，可以打断相关成员。&lt;/li&gt;
  &lt;li&gt;协同工作所需的聊天，应该只打扰相关的协同方；其他人可以关注进展，但不应该被打扰。&lt;/li&gt;
  &lt;li&gt;工作期间也可以吹水，但这些信息一定不能不断打断团队成员。&lt;/li&gt;
  &lt;li&gt;还有其他的消息过滤级别。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，我们在手机上可以通过 App 的不同来进行消息分级，避免不同种类的消息交叉干扰。在 PC 上通过任务栏上不同的软件来避免消息的交叉干扰；甚至工作期间在 PC 上可以不启动非工作用的软件。&lt;/p&gt;

&lt;h2 id=&quot;考虑使用工作专用的-im-工具&quot;&gt;考虑使用工作专用的 IM 工具&lt;/h2&gt;

&lt;p&gt;TIM 相比于 QQ，提供了更适合于工作场合的 UI/UX。可以让我们更加专注于工作本身而不必被 QQ 的其他功能打扰。不过 TIM 和 QQ 的消息是完全互通的，这样就做不到消息的自动过滤，依然存在消息之间的交叉干扰。&lt;/p&gt;

&lt;p&gt;企业微信与微信的消息不是直接互通的，而是作为企业外部人员来对待。钉钉是一款全新的 IM，所以消息自然不会互通。这就断开了企业内部的消息和其他消息的交叉干扰。当工作时，会只注重由企业微信或者钉钉发来的消息，微信或者 QQ 的消息就可以延迟查看；而离开工作时，可以延迟关注来自于企业微信或者钉钉发来的消息，更加关注朋友与技术社区，专注于情感的建立与个人的成长。&lt;/p&gt;

&lt;p&gt;反正，无论你用什么，只要不是 QQ 或者微信，团队的 IM 效率就能得到提升。&lt;/p&gt;

&lt;h2 id=&quot;考虑使用-slack&quot;&gt;考虑使用 Slack&lt;/h2&gt;

&lt;p&gt;可以试试 Slack，这也是一款以 IM 为主的团队协作工具。&lt;/p&gt;

&lt;p&gt;不过，它还带了更多的扩展 API 可以使用，可以接入到团队正在使用的各种系统中来。帮助我们将大量的手工任务改造成自动化完成的任务。&lt;/p&gt;
</description>
        <pubDate>Sun, 06 Jan 2019 10:52:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/why-we-need-another-im-tool-only-for-work.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/why-we-need-another-im-tool-only-for-work.html</guid>
        
        
        <category>team</category>
        
      </item>
    
      <item>
        <title>Slack 开发入门之 Incoming Webhooks：往 Slack 的 Channel 中发消息</title>
        <description>&lt;p&gt;一个工程师团队使用 Slack 进行团队协作比 QQ / 微信流的效率高多啦。除了基本的 IM 之外，它的扩展性也是非常重要的一点。&lt;/p&gt;

&lt;p&gt;本文介绍 Slack 的开发入门：Incoming Webhooks 篇。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建一个新-slack-应用&quot;&gt;创建一个新 Slack 应用&lt;/h2&gt;

&lt;p&gt;如果你已经创建了一个 Slack 应用，可以跳过这一节。&lt;/p&gt;

&lt;p&gt;在这里 &lt;a href=&quot;https://api.slack.com/apps/new&quot;&gt;https://api.slack.com/apps/new&lt;/a&gt; 创建一个新的 Slack 应用：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-14-32.png&quot; alt=&quot;填写新应用信息&quot; /&gt;&lt;/p&gt;

&lt;p&gt;填写完两个信息之后，你就可以选择五种不同的应用类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Incoming Webhooks
    &lt;ul&gt;
      &lt;li&gt;Post messages from external sources into Slack.&lt;/li&gt;
      &lt;li&gt;将外部的资源作为一个消息发送到 Slack 中。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Interactive Components
    &lt;ul&gt;
      &lt;li&gt;Add buttons to your app’s messages, and create an interactive experience for users.&lt;/li&gt;
      &lt;li&gt;为 Slack 中消息添加一个按钮，以便让你的应用与用户之间可以有交互。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Slash Commands
    &lt;ul&gt;
      &lt;li&gt;Allow users to perform app actions by typing commands in Slack.&lt;/li&gt;
      &lt;li&gt;允许用户在 Slack 中敲入命令来控制应用的行为。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Event Subscriptions
    &lt;ul&gt;
      &lt;li&gt;Make it easy for your app to respond to activity in Slack.&lt;/li&gt;
      &lt;li&gt;允许你的应用响应 Slack 中的一些活动。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Bots
    &lt;ul&gt;
      &lt;li&gt;Add a bot to allow users to exchange messages with your app.&lt;/li&gt;
      &lt;li&gt;开发一个机器人，与 Slack 中的其他人进行交流。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Permissions
    &lt;ul&gt;
      &lt;li&gt;Configure permissions to allow your app to interact with the Slack API.&lt;/li&gt;
      &lt;li&gt;管理你的应用与 Slack API 之间的权限。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-16-48.png&quot; alt=&quot;五种不同的应用类型&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;创建-webhooks-应用&quot;&gt;创建 Webhooks 应用&lt;/h2&gt;

&lt;p&gt;本文，我们选中 Incoming Webhooks。&lt;/p&gt;

&lt;p&gt;或者如果这已经是你创建好的应用了，可以左边的列表中选择 Incoming Webhooks。&lt;/p&gt;

&lt;p&gt;然后按一下右上角的激活按钮，使得 Incoming Webhooks 功能激活。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-23-21.png&quot; alt=&quot;激活&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果顶部有提示因为权限问题需要重新安装，那么就点进去重新安装。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-15-28-41.png&quot; alt=&quot;提示重新安装&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;添加一个-webhook-url&quot;&gt;添加一个 Webhook Url&lt;/h2&gt;

&lt;p&gt;继续把网页往下滑，点击 [Add New Webhook to Workspace]。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-27-28.png&quot; alt=&quot;添加一个 Webhook Url&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后选择需要发消息的 Channel：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-29-26.png&quot; alt=&quot;添加一个可以发消息的 Channel&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，页面还会继续回到添加 Url 的地方，但示例 Demo 已经换上了真实的 Url，而且你可以复制到剪贴板。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-31-14.png&quot; alt=&quot;可以复制的新 Url&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;模拟发送一个消息&quot;&gt;模拟发送一个消息&lt;/h2&gt;

&lt;p&gt;为了迅速验证，我们可以使用 Postman 来发送这条消息。&lt;/p&gt;

&lt;p&gt;关于下载和使用 Postman，你可以参考我的另一篇博客：&lt;a href=&quot;/post/use-postman-to-debug-asp-net-core-api&quot;&gt;使用 Postman 调试 ASP.NET Core 开发的 API&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;填写要 POST 的 Url，然后在消息的 Body 中填写 JSON 格式的消息内容：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hi! 给你个 **任务** 玩玩。&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-35-41.png&quot; alt=&quot;使用 Postman&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，点击 Send 按钮，消息发送成功。&lt;/p&gt;

&lt;p&gt;于是我的 Slack 通道中收到了一条来自这个应用发来消息：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2019-01-06-14-38-26.png&quot; alt=&quot;看看新发送的消息&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;你可以用这个来做什么&quot;&gt;你可以用这个来做什么？&lt;/h2&gt;

&lt;p&gt;当你可以随时向 Slack 的某个通道发送消息之后，你可以用来做什么呢？&lt;/p&gt;

&lt;p&gt;你可以定时发送团队的代码审查发送发现的问题，可以发送自动化编译失败的信息，可以发送每周的任务计划和总结，等等。&lt;/p&gt;

&lt;p&gt;当然，Slack 上本身就提供了大量的应用可以直接下载安装，自己做开发是解决更定制化的需求。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://api.slack.com/bot-users&quot;&gt;Enabling interactions with bots - Slack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 06 Jan 2019 07:29:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/slack-api-starter-incoming-webhooks.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/slack-api-starter-incoming-webhooks.html</guid>
        
        
        <category>slack</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>WPF 中那些可跨线程访问的 DispatcherObject（WPF Free Threaded Dispatcher Object）</title>
        <description>&lt;p&gt;众所周知的，WPF 中多数对象都继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 带给这些对象一个特点：不能跨线程访问。&lt;/p&gt;

&lt;p&gt;不过，WPF 中依然存在一些例外。本文将介绍 WPF 那些可跨线程访问的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;，如何充分利用这个特点提高应用程序的性能，以及如何自己编写这样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;什么样的-dispatcherobject-可以跨线程访问&quot;&gt;什么样的 DispatcherObject 可以跨线程访问？&lt;/h2&gt;

&lt;p&gt;要了解什么样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 可以跨线程访问，需要知道 WPF 是如何限制对象的跨线程访问的。&lt;/p&gt;

&lt;h3 id=&quot;dispatcher-属性&quot;&gt;Dispatcher 属性&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 类有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性，它长下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This property is free-threaded.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;属性在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的构造函数中被赋值：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;checkaccess-和-verifyaccess&quot;&gt;CheckAccess 和 VerifyAccess&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 提供了两种验证 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的方法，&lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt;；他们内部的实现是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 用于检查调用线程对此对象是否有访问权，如果有访问权，则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，否则返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 也是用于检查调用线程对此对象是否有访问权，如果没有访问权会抛出异常。&lt;/p&gt;

&lt;p&gt;你可以阅读这两个方法的代码来了解其实现原理。每个方法只有短短的一两行而已，非常容易理解。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Checks that the calling thread has access to this object.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Only the dispatcher thread may access DispatcherObjects.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     &amp;lt;p/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     This method is public so that any thread can probe to&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     see if it has access to the DispatcherObject.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     True if the calling thread has access to this object.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Verifies that the calling thread has access to this object.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Only the dispatcher thread may access DispatcherObjects.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     &amp;lt;p/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     This method is public so that derived classes can probe to&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     see if the calling thread has access to itself.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SRID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要说明的是，只有调用这两个方法才会对线程的访问权限进行检查。如果你写一个类继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 而在你的属性和方法中不直接或间接调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt;，那么这是不受线程访问限制的。&lt;/p&gt;

&lt;p&gt;只不过，WPF 封装的大多对象和属性都调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt;（例如依赖项属性），所以很大程度上限制了 WPF UI 的线程访问权限。&lt;/p&gt;

&lt;h3 id=&quot;_dispatcher-的重新赋值&quot;&gt;_dispatcher 的重新赋值&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性的获取实际上就是在拿 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段的值。于是我们现在仔细寻找 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 的所有赋值代码，只有三处，就是下面这三个方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;构造函数，会赋值为当前线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt;，会赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MakeSentinel&lt;/code&gt;，会赋值为另一个线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的值（即一个线程创建，但由另一个线程来使用）。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Instantiate this object associated with the current Dispatcher.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This method allows certain derived classes to break the dispatcher affinity&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// of our objects.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DetachFromDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Make this object a &quot;sentinel&quot; - it can be used in equality tests, but should&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// not be used in any other way.  To enforce this and catch bugs, use a special&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// sentinel dispatcher, so that calls to CheckAccess and VerifyAccess will&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// fail;  this will catch most accidental uses of the sentinel.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FriendAccessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Built into Base, also used by Framework.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureSentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureSentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// lazy creation - the first thread reaching here creates the sentinel&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// dispatcher, all other threads use it.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinelDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isSentinel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sentinelDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们发现，实际上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性虽然在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 对象创建的时候会赋值，但实际上提供了多种方法来修改值。有的是修改成另一个线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;，而有的就是粗暴地赋值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;_dispatcher-赋值为-null&quot;&gt;_dispatcher 赋值为 null&lt;/h3&gt;

&lt;p&gt;无论是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 方法，实际上都对 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 进行了判断。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This method is free-threaded.&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Note: a DispatcherObject that is not associated with a&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// dispatcher is considered to be free-threaded.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CheckAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessAllowed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This method is free-threaded.&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Note: a DispatcherObject that is not associated with a&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// dispatcher is considered to be free-threaded.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注释中说明：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: a DispatcherObject that is not associated with a dispatcher is considered to be free-threaded.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，如果一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 对象没有任何被关联的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;，那么就被认为这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 没有线程访问限制，此对象将允许被任何线程访问。&lt;/p&gt;

&lt;h2 id=&quot;哪些-dispatcherobject-是可以跨线程访问的&quot;&gt;哪些 DispatcherObject 是可以跨线程访问的？&lt;/h2&gt;

&lt;p&gt;通过阅读 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的源码，我们可以知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 其实是允许跨线程访问的，它只是在刚刚创建的时候如果没有其他额外的方法调用使得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性改变，那么就只能被创建它的线程访问。&lt;/p&gt;

&lt;p&gt;但也需要注意，能够改变 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性值的两个方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeSentinel&lt;/code&gt; 都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 的。这意味着只有微软自己在 WindowsBase、PresentationCore 和 PresentationFramework 程序集中编写的类型才能修改其值。可是，有哪些类呢？&lt;/p&gt;

&lt;p&gt;通过查找 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 的引用，我找到了以下类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StyleHelper&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TriggerBase&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeginStoryboard&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceDictionary&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;也就是说，这些类型的实例会在某种特定的条件下从单线程访问权限变为可被任意跨线程访问。（实际上 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceDictionary&lt;/code&gt; 并不是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt;，不过它会访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;Owner&lt;/code&gt;，这是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject&lt;/code&gt;；所以也会涉及到一点跨线程问题。）&lt;/p&gt;

&lt;p&gt;而查找 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeSentinel&lt;/code&gt; 的引用，又可以找到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemsControl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemsControl&lt;/code&gt; 类在某种情况下提供了一种在一个线程中创建对象，在另外一个线程中使用的特性。&lt;/p&gt;

&lt;h3 id=&quot;freezable&quot;&gt;Freezable&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 是继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的一个抽象类，其出现的主要目的就是解决 WPF 单线程模型带来的负面性能影响。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 主要由那些与图形渲染强相关的 WPF 类型继承，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Brush&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;D3DImage&lt;/code&gt; 还有各种动画等。典型的，这些类型都对高性能渲染有要求。这些类型的刚开始创建的时候只能由创建的对象对它进行修改，而且在修改的时候还会引发 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 事件以便相关类型对其进行处理。不过，一旦 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freeze&lt;/code&gt;，这些类型将变成不可修改，这时不会也不需要再引发 &lt;code class=&quot;highlighter-rouge&quot;&gt;Changed&lt;/code&gt; 事件，可以提升性能，同时对所有线程开放访问权限，这样能继续提升性能。&lt;/p&gt;

&lt;p&gt;你可以对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 对象调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freeze()&lt;/code&gt; 方法，调用之后，其 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性会被设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，于是对象可以跨线程访问。&lt;/p&gt;

&lt;p&gt;在 XAML 中，你可以通过指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationOptions:Freeze&lt;/code&gt; 特性达到同样的目的。如下面的例子，&lt;code class=&quot;highlighter-rouge&quot;&gt;SolidColorBrush&lt;/code&gt; 对象在创建完设置完所有的值之后，会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freeze&lt;/code&gt; 冻结这个对象以便跨线程访问。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:PresentationOptions=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation/options&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PresentationOptions&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 如果你的 mc:Ignorable 有多个，请用空格隔开。 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Page.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 注意，在 Resource 中的 SolidColorBrush 默认情况下是不会自动 Freeze 的， --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!--     但是，你可以通过指定 PresentationOptions:Freeze 特性使得它在创建完后 Freeze。 --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 对象在 Resources 中不会自动创建，它会在第一次被使用的时候创建， --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!--     也就是说，你如果要验证它的跨线程访问，需要使用两个不同的线程访问它。 --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Brush.Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PresentationOptions:Freeze=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Red&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page.Resources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于以上代码，有一些是需要说明的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果你的 mc:Ignorable 有多个，请用空格隔开。&lt;/li&gt;
  &lt;li&gt;在 Resource 中的 SolidColorBrush 默认情况下是不会自动 Freeze 的；但是，你可以通过指定 PresentationOptions:Freeze 特性使得它在创建完后 Freeze。&lt;/li&gt;
  &lt;li&gt;对象在 Resources 中不会自动创建，它会在第一次被使用的时候创建；也就是说，你如果要验证它的跨线程访问，需要使用两个不同的线程访问它（仅仅用一个后台线程去验证它，你会发现后台线程依然能够正常访问它的依赖项属性的值）。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;style&quot;&gt;Style&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 是直接继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的类型，并没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freeze&lt;/code&gt; 相关的方法。不过这不重要，因为重要的是能够访问到内部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 的代码在 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt; 方法中，这是继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ISealable&lt;/code&gt; 接口的方法。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;internal interface ISealable
{
    bool CanSeal { get; }
    void Seal();
    bool IsSealed { get; }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了方便理解，我把 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt; 方法进行简化后贴在下面：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// This Style and all factories/triggers are now immutable&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Verify Context Access&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;VerifyAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 99% case - Style is already sealed.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sealed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 省略一些验证代码。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal setters&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_setters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_setters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal triggers&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_visualTriggers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_visualTriggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Will throw InvalidOperationException if we find a loop of&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  BasedOn references.  (A.BasedOn = B, B.BasedOn = C, C.BasedOn = A)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;CheckForCircularBasedOnReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal BasedOn Style chain&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_basedOn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_basedOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal the ResourceDictionary&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_resources&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsReadOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Build shared tables&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Process all Setters set on the selfStyle. This stores all the property&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// setters on the current styles into PropertyValues list, so it can be used&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// by ProcessSelfStyle in the next step. The EventSetters for the current&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and all the basedOn styles are merged into the EventHandlersStore on the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// current style.&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ProcessSetters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Add an entry in the EventDependents list for&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// the TargetType's EventHandlersStore. Notice&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// that the childIndex is 0.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StyleHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventDependent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandlersStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Process all PropertyValues (all are &quot;Self&quot;) in the Style&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// chain (base added first)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ProcessSelfStyles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Process all TriggerBase PropertyValues (&quot;Self&quot; triggers&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and child triggers) in the Style chain last (highest priority)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ProcessVisualTriggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Sort the ResourceDependents, to help avoid duplicate invalidations&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StyleHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SortResourceDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// All done, seal self and call it a day.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_sealed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Remove thread affinity so it can be accessed across threads&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DetachFromDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体来说，就是将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 中的所有属性进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt;，将资源设为只读；然后，将自己的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;template&quot;&gt;Template&lt;/h3&gt;

&lt;p&gt;不过，我们通常使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 的方式都是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 中写控件模板。如果控件模板不支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 即便 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt;，多数情况下也是没有用的。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;StyleHelper&lt;/code&gt; 类型中，处理了控件模板的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;它处理的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkTemplate&lt;/code&gt;，这是控件模板的基类，具体来说，有这些类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemsPanelTemplate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemContainerTemplate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;HierarchicalDataTemplate&lt;/li&gt;
  &lt;li&gt;ContentPresenter.DefaultTemplate&lt;/li&gt;
  &lt;li&gt;ContentPresenter.UseContentTemplate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下是 &lt;code class=&quot;highlighter-rouge&quot;&gt;StyleHelper.SealTemplate&lt;/code&gt; 方法。这里原本是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkTemplate&lt;/code&gt; 内部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt; 方法的实现，不过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt; 内部调到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;StyleHelper.SealTemplate&lt;/code&gt; 静态方法了。&lt;/p&gt;

&lt;p&gt;为了便于理解，我也对其进行了精简。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SealTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FrameworkTemplate&lt;/span&gt;                                           &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;                                                    &lt;span class=&quot;n&quot;&gt;isSealed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FrameworkElementFactory&lt;/span&gt;                                     &lt;span class=&quot;n&quot;&gt;templateRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TriggerCollection&lt;/span&gt;                                           &lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ResourceDictionary&lt;/span&gt;                                          &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HybridDictionary&lt;/span&gt;                                            &lt;span class=&quot;n&quot;&gt;childIndexFromChildID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrugalStructList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChildRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;                           &lt;span class=&quot;n&quot;&gt;childRecordFromChildIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrugalStructList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemStructMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TriggerSourceRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;triggerSourceRecordFromChildIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrugalStructList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainerDependent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;                    &lt;span class=&quot;n&quot;&gt;containerDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrugalStructList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChildPropertyDependent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;resourceDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemStructList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChildEventDependent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;                     &lt;span class=&quot;n&quot;&gt;eventDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HybridDictionary&lt;/span&gt;                                        &lt;span class=&quot;n&quot;&gt;triggerActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HybridDictionary&lt;/span&gt;                                        &lt;span class=&quot;n&quot;&gt;dataTriggerRecordFromBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;                                                    &lt;span class=&quot;n&quot;&gt;hasInstanceValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandlersStore&lt;/span&gt;                                      &lt;span class=&quot;n&quot;&gt;eventHandlersStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This template has already been sealed. There is no more to do.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isSealed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal template nodes (if exists)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessTemplateBeforeSeal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;templateRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;templateRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal triggers&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Seal Resource Dictionary&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsReadOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//  Build shared tables&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;StyleHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessTemplateTriggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childRecordFromChildIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;triggerSourceRecordFromChildIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventDependents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataTriggerRecordFromBinding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childIndexFromChildID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasInstanceValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;triggerActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;templateRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventHandlersStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyTriggersWithActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataTriggersWithActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasLoadedChangeHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetResourceReferenceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// All done, seal self and call it a day.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;isSealed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Remove thread affinity so it can be accessed across threads&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frameworkTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DetachFromDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;frameworkTemplate.DetachFromDispatcher()&lt;/code&gt; 方法即调用基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;方法内部也是对各种属性进行了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt; 和只读化处理。最后，将自己的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;style-和-template&quot;&gt;Style 和 Template&lt;/h3&gt;

&lt;p&gt;由于每次应用模板的时候，都是创建新的 UI 控件，所以实际上通过模板创建的 UI 对象并不会产生跨线程访问的问题。也就是说，当 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt; 设置为可跨线程访问之后，是可以被多个线程同时访问创建控件而不会产生跨线程访问的问题。&lt;/p&gt;

&lt;p&gt;写在 XAML 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ISealable&lt;/code&gt; 在创建的时候就会执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal()&lt;/code&gt;。也就是说，你只要在 XAML 中写下了这个对象，那么就会在创建完后 &lt;code class=&quot;highlighter-rouge&quot;&gt;Seal&lt;/code&gt;。这点跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 是不一样的，&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 是需要自己主动编写 XAML 或 C# 代码进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freeze&lt;/code&gt; 的。&lt;/p&gt;

&lt;p&gt;从这里可以推论出，你在 XAML 中写的样式，可以被跨线程访问而不会出现线程安全问题。&lt;/p&gt;

&lt;h2 id=&quot;强制让一个-dispatcherobject-跨线程访问&quot;&gt;强制让一个 DispatcherObject 跨线程访问&lt;/h2&gt;

&lt;p&gt;从前面的各种源码分析来看，使用常规方法让任意一个对象进行跨线程访问几乎是不可能的了。剩下的就只是做一些邪恶的事情了，比如 —— 反射。&lt;/p&gt;

&lt;p&gt;可以反射调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DetachFromDispatcher&lt;/code&gt; 方法，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性值清空，这样的对象将可以跨所有线程访问。不过要小心，你随意写的对象可能实际上是不具备跨线程访问能力的，这样的修改可能导致线程安全问题，你需要自行承担后果。&lt;/p&gt;

&lt;p&gt;可以反射直接修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;_dispatcher&lt;/code&gt; 字段的值，改为目标线程中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;。这样的做法只是切换了一个线程，效果和调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MakeSentinel&lt;/code&gt; 是一样的。使用这样的方式可以让创建对象的线程和使用对象的线程分开，适用于创建对象需要花费大量时间的对象 —— 如 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitmapImage&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;特别的，如果你的对象中有子 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 对象，你需要像上面的源码那样将所有子对象的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性都进行替换才行。&lt;/p&gt;

&lt;p&gt;为了方便，我写了一个辅助方法来完成这样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 属性值切换。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Windows.Threading&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含 &amp;lt;see cref=&quot;DispatcherObject&quot;/&amp;gt; 及其派生类对象切换所属线程的相关方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherSwitcher&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 延迟初始化 &amp;lt;see cref=&quot;Dispatcher&quot;/&amp;gt; 类型中的 _dispatcher 字段。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FieldInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherFieldInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FieldInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_dispatcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NonPublic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;LazyThreadSafetyMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 将指定的 &amp;lt;see cref=&quot;DispatcherObject&quot;/&amp;gt; 对象的所属线程切换至指定调度器所属的线程。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 注意：如果此对象包含嵌套的其他对象，则极有可能会发生跨线程访问的异常，请谨慎使用！&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;d&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;dispatcher&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SwitchTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherFieldInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FieldAccessException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;为什么 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 可以限制跨线程访问？
    &lt;ul&gt;
      &lt;li&gt;因为内部有 &lt;code class=&quot;highlighter-rouge&quot;&gt;CheckAccess&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 方法检查线程的访问权限&lt;/li&gt;
      &lt;li&gt;众多子类的属性和方法在使用前调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 来验证调用方的线程&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;在 XAML 中编写的代码时，定义在 FrameworkElement 的 Resources 中的对象，哪些可以跨线程访问，哪些不可以跨线程访问？
    &lt;ul&gt;
      &lt;li&gt;非继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的对象可以跨线程访问（这里就不要钻牛角尖说自己写的烂类了）
        &lt;ul&gt;
          &lt;li&gt;比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;system:String&amp;gt;Walterlv&amp;lt;/system:String&amp;gt;&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 的对象，但是同时实现了内部 &lt;code class=&quot;highlighter-rouge&quot;&gt;ISealable&lt;/code&gt; 接口的对象
        &lt;ul&gt;
          &lt;li&gt;比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ItemTemplate&lt;/code&gt; 等&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt; 的对象
        &lt;ul&gt;
          &lt;li&gt;指定了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationOptions:Freeze=&quot;True&quot;&lt;/code&gt; 特性的对象可以跨线程访问&lt;/li&gt;
          &lt;li&gt;没有指定此特性的对象不可以跨线程访问&lt;/li&gt;
          &lt;li&gt;这些对象比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Brush&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Transform&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Geometry&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;D3DImage&lt;/code&gt; 还有各种动画等&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;其他 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 对象
        &lt;ul&gt;
          &lt;li&gt;不可以跨线程访问（当然你自己写的类型，没有访问基类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;VerifyAccess&lt;/code&gt; 的话就没事）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;可以随意切换 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherObject&lt;/code&gt; 关联的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 吗？
    &lt;ul&gt;
      &lt;li&gt;不可以随意切换，因为切换关联 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的方法都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 的&lt;/li&gt;
      &lt;li&gt;不过我们可以使用反射来间接实现这个效果（当然，你需要自行承担线程安全后果，以及切换不完全造成的跨线程访问问题）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/freezable-objects-overview?wt.mc_id=MVP&quot;&gt;Freezable Objects Overview - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/mc-ignorable-attribute?wt.mc_id=MVP&quot;&gt;mc:Ignorable Attribute - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherObject.cs,85082c8bba6e8038&quot;&gt;DispatcherObject.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 03 Jan 2019 01:03:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-free-threaded-dispatcher-object.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-free-threaded-dispatcher-object.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET 使用 XPath 来读写 XML 文件（顺带解决 XML 命名空间的问题）</title>
        <description>&lt;p&gt;XPath 是 XML 路径语言（XML Path Language），用来确定XML文档中某部分位置的语言。无论是什么语言什么框架，几乎都可以使用 XPath 来高效查询 XML 文件。&lt;/p&gt;

&lt;p&gt;本文将介绍 .NET 中的 XPath 相关类型的使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文读写的 XML 文件会以 &lt;a href=&quot;#%E5%81%87%E8%AE%BE%E7%9A%84-xml-%E6%96%87%E4%BB%B6&quot;&gt;文章末尾的代码 - 假设的 XML 文件&lt;/a&gt; 作为示例。&lt;/p&gt;

&lt;p&gt;关于 XPath 语法，可以阅读 &lt;a href=&quot;/post/xml-xpath&quot;&gt;XML 的 XPath 语法&lt;/a&gt; 了解更多。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一切从这里开始&quot;&gt;一切从这里开始&lt;/h2&gt;

&lt;p&gt;.NET 中支持 XPath 的 XML 文档类有两种读取方法，一种是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPathDocument&lt;/code&gt;，以只读的方式读取；另一种是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlDocument&lt;/code&gt;，不止可以读，还可以编辑。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 得到 walterlv.xml 文档在内存中的快速只读表示形式。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xPathDocument&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XPathDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 以可读可写的方式打开 walterlv.xml 文件。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlDocument&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;  
&lt;span class=&quot;n&quot;&gt;xmlDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果要确定 XML 的文件编码，需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlTextReader&lt;/code&gt; 来读 XML 文件；它的基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlReader&lt;/code&gt; 没有提供编码信息。&lt;code class=&quot;highlighter-rouge&quot;&gt;XmlTextReader&lt;/code&gt; 作为参数传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPathDocument&lt;/code&gt; 的构造函数或 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlDocument.Load&lt;/code&gt; 方法中即可。&lt;/p&gt;

&lt;p&gt;无论是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPathDocument&lt;/code&gt; 还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlDocument&lt;/code&gt;，因为都实现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IXPathNavigable&lt;/code&gt;，所以都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNavigator();&lt;/code&gt; 方法，调用能得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPathNavigator&lt;/code&gt; 对象。不过前者的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanEdit&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，后者的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanEdit&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;navigator1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xPathDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateNavigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;navigator2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateNavigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;上手-xpath&quot;&gt;上手 XPath&lt;/h2&gt;

&lt;h3 id=&quot;路径查询&quot;&gt;路径查询&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;XPathNavigator&lt;/code&gt; 对象提供了下面两种通用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPath&lt;/code&gt; 表达式的使用检索方法。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SelectSingleNode&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;比如希望检索本文末尾的 XML 文件中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt;，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;/package/metadata/id&lt;/code&gt; 即可检索。&lt;/p&gt;

&lt;p&gt;当然，事实上这个 XML 文件是不能这样检索出来 &lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt; 的，因为它带有命名空间。&lt;/p&gt;

&lt;p&gt;带有命名空间的检索需要使用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlNamespaceManager&lt;/code&gt; 类，并写成下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namespaceManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlNamespaceManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NameTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;namespaceManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/d:package/d:metadata/d:id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namespaceManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里其实略微奇怪，因为命名 &lt;code class=&quot;highlighter-rouge&quot;&gt;package&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt; 等都在默认的命名空间下，我们却必须显式加一个命名空间前缀。微软对此的解释是如果不指定命名空间前缀，默认都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，而不是 XML 声明的那个默认命名空间。&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/data/xml/xpath-queries-and-namespaces#the-default-namespace?wt.mc_id=MVP&quot;&gt;这里是原文&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;XPath treats the empty prefix as the &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; namespace. In other words, only prefixes mapped to namespaces can be used in XPath queries. This means that if you want to query against a namespace in an XML document, even if it is the default namespace, you need to define a prefix for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;路径检索的语法也有很多种，可以参考我的另一篇文章 &lt;a href=&quot;/post/xml-xpath&quot;&gt;XML 的 XPath 语法&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;为了提升性能，&lt;code class=&quot;highlighter-rouge&quot;&gt;XPathNavigator&lt;/code&gt; 额外提供了这些方法，用于替代 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPath&lt;/code&gt; 中的部分对应的语法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SelectChildren&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SelectAncestors&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SelectDescendants&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;xpath-函数调用&quot;&gt;XPath 函数调用&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Evaluate&lt;/code&gt; 提供了复杂的 XPath 函数调用。比如下面我们把几种 url 都拼接在一起得到一个新字符串。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;XPathExpression&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;concat(//licenseUrl/text(), //projectUrl/text(), //iconUrl/text())&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;节点匹配&quot;&gt;节点匹配&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Matches&lt;/code&gt; 用来检查当前的节点是否满足某个条件。比如下面的例子便是检查当前节点的父节点是否是 &lt;code class=&quot;highlighter-rouge&quot;&gt;group&lt;/code&gt; 并且其 &lt;code class=&quot;highlighter-rouge&quot;&gt;targetFramework&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;.NETStandard2.0&lt;/code&gt;。显然，符合这个条件的只有最后的那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;dependency&lt;/code&gt; 节点。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;../group/@targetFramework='.NETStandard2.0'&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;xpath-导航&quot;&gt;XPath 导航&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;XPathNavigator&lt;/code&gt; 可以在节点、属性中间移动，以便能够不止从根节点进行查询。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveTo&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToChild&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToFirst&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToFirstChild&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToFollowing&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToId&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToNext&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToParent&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToPrevious&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToRoo&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToAttribute&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToFirstAttribute&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToNextAttribute&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToNamespace&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToFirstNamespace&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveToNextNamespace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在导航到需要的节点或者属性后，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;navigator.OuterXml&lt;/code&gt; 拿到节点的所有 XML 字符串。也可以使用下面这些方法拿到节点内部的值。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAsBoolean&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAsDateTime&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAsDouble&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAsInt&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAsLong&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueAs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;编辑-xml&quot;&gt;编辑 XML&lt;/h2&gt;

&lt;p&gt;由于我们要编辑 XML 数据，所以加载 XML 文件的方式不能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XPathDocument&lt;/code&gt; 了，得是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlDocument&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;插入使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Insert&lt;/code&gt; 相关的方法，删除使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delete&lt;/code&gt; 相关的方法。而修改数据使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetValue&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;保存-xml-到文件&quot;&gt;保存 XML 到文件&lt;/h2&gt;

&lt;p&gt;保存 XML 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlDocument&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Save&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteTo&lt;/code&gt; 方法即可。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;假设的-xml-文件&quot;&gt;假设的 XML 文件&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;metadata&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;MSTestEnhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;owners&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/owners&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;requireLicenseAcceptance&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/requireLicenseAcceptance&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;licenseUrl&amp;gt;&lt;/span&gt;https://github.com/easiwin/MSTestEnhancer/blob/master/LICENSE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/licenseUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;projectUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/projectUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;iconUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer/icon.png&lt;span class=&quot;nt&quot;&gt;&amp;lt;/iconUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;MSTestEnhancer helps you to write unit tests without naming any method. You can write method contract descriptions instead of writing confusing test method name when writing unit tests.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;releaseNotes&amp;gt;&lt;/span&gt;Support passing null into WithArgument method.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/releaseNotes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;Copyright (c) 2018 dotnet职业技术学院&lt;span class=&quot;nt&quot;&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;repository&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/easiwin/MSTestEnhancer.git&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETStandard2.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/metadata&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/standard/data/xml/select-nodes-using-xpath-navigation?wt.mc_id=MVP&quot;&gt;使用 XPath 导航选择节点 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/data/xml/process-xml-data-using-the-xpath-data-model?wt.mc_id=MVP&quot;&gt;Process XML Data Using the XPath Data Model - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/data/xml/xpath-queries-and-namespaces?wt.mc_id=MVP&quot;&gt;XPath Queries and Namespaces - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mgenware.com/blog/?p=596&quot;&gt;.NET(C#)：使用XPath查询带有命名空间(有xmlns)的XML - Mgen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/3642829/6233938&quot;&gt;.net - How to use XPath with XElement or LINQ? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 01 Jan 2019 03:34:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/read-write-xml-using-xpath-in-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/read-write-xml-using-xpath-in-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>基于 task 为 VSCode 添加自定义的外部命令</title>
        <description>&lt;p&gt;我们有很多全局的工具能在各处使用命令行调用，针对某个仓库特定的命令可以放到仓库中。不过，如果能够直接为顺手的文本编辑器添加自定义的外部命令，那么执行命令只需要简单的快捷键即可，不需要再手工敲了。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;写一个外部命令的调用&quot;&gt;写一个外部命令的调用&lt;/h2&gt;

&lt;p&gt;由于是调用外部工具，所以工具本身用什么语言写已经不重要的了，只要有环境，没有什么是不能执行的。&lt;/p&gt;

&lt;p&gt;这里以我博客中使用的外部命令 &lt;a href=&quot;https://github.com/dotnet-campus/markdown-metadata&quot;&gt;mdmeta&lt;/a&gt; 为例。我将此工具使用 mklink 命令链接到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;/build&lt;/code&gt; 文件夹中（当然，如果需要多人协作开发，可以使用 git-lfs 或者 git-submodule 来管理仅项目的外部命令）。关于 mklink 的使用，可以参考 &lt;a href=&quot;/post/problems-of-mklink&quot;&gt;解决 mklink 使用中的各种坑（硬链接，软链接/符号链接，目录链接）&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;于是，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;/build&lt;/code&gt; 文件夹中添加可执行的脚本，例如：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build\mdmeta\mdmeta.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wupdate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--ignore-in-hour&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;让-vscode-使用此外部命令&quot;&gt;让 VSCode 使用此外部命令&lt;/h2&gt;

&lt;p&gt;如果你说使用 VSCode 嵌入的终端来使用外部命令，那我们其实没做什么，就像使用普通的脚本或者命令一样。&lt;/p&gt;

&lt;p&gt;但是，VSCode 自带有 Tasks 机制，可以将命令与 VSCode 集成。关于 Tasks，可以阅读 VSCode 的官方文档：&lt;a href=&quot;https://code.visualstudio.com/docs/editor/tasks&quot;&gt;Tasks in Visual Studio Code&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;具体来说，是写一个配置文件 /.vscode/tasks.json。&lt;/p&gt;

&lt;p&gt;tasks.json 中有少量的默认内容，如果你完全不知道如可开始编写，可以按 F1，选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Configure ...&lt;/code&gt; 随便配置一个 Task，然后基于它修改。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-12-20-35-30.png&quot; alt=&quot;F1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里，我定义了两个命令：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Init Metadata&lt;/li&gt;
  &lt;li&gt;Update Metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;See&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;https://go.microsoft.com/fwlink/?LinkId=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;733558&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;documentation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;tasks.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tasks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Init Metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceRoot}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mdinit.ps1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;problemMatcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Update Metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceRoot}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mdupdate.ps1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;problemMatcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isDefault&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，这是按照我自己的需求写了两个命令，前者用来初始化我的博客仓库，后者用来更新我所有博客文章的 YAML 元数据。&lt;/p&gt;

&lt;p&gt;由于后者才是需要频繁使用的命令，所以我将其设为编译类型的命令（&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;kind&quot;: &quot;build&quot;&lt;/code&gt;）。具体来说，设定为编译类型并指定为默认（&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;isDefault&quot;: true&lt;/code&gt;）将获得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+Shift+B&lt;/code&gt; 快捷键的原生支持。&lt;/p&gt;

&lt;h2 id=&quot;使用快捷键执行外部命令&quot;&gt;使用快捷键执行外部命令&lt;/h2&gt;

&lt;p&gt;当然，如果你有其他的编译命令，或者你有很多个命令，可以自己指定快捷键。比如我希望按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+U&lt;/code&gt; 时更新我的元数据（即执行以上第二条命令），直接在命令上加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;key&quot;: &quot;ctrl+u&quot;&lt;/code&gt; 即可。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Update Metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ctrl+u&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${workspaceRoot}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mdupdate.ps1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;problemMatcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isDefault&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/tasks&quot;&gt;Tasks in Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 30 Dec 2018 09:00:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-run-external-executable-tools-for-vscode.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-run-external-executable-tools-for-vscode.html</guid>
        
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>.NET 中使用 Mutex 进行跨越进程边界的同步</title>
        <description>&lt;p&gt;Mutex 是 Mutual Exclusion 的缩写，是互斥锁，用于防止两个线程同时对计算机上的同一个资源进行访问。不过相比于其他互斥的方式，Mutex 能够跨越线程边界。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;mutex-是什么&quot;&gt;Mutex 是什么？&lt;/h2&gt;

&lt;p&gt;与其他线程同步的方式一样，Mutex 也提供对资源的互斥访问；不过 Mutex 使用的系统资源会比 Monitor 更多，而 Monitor 就是实现 C# 中 lock 关键字所用的锁。&lt;/p&gt;

&lt;p&gt;用更多的系统资源，带来更强大的功能 —— Mutex 能进行跨越应用程序域边界的封送，能进行跨越进程边界的线程同步。&lt;/p&gt;

&lt;h2 id=&quot;简单的-mutex不能跨进程互斥&quot;&gt;简单的 Mutex（不能跨进程互斥）&lt;/h2&gt;

&lt;p&gt;最简单的 Mutex 的使用方法就是直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来，然后使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait&lt;/code&gt; 进行等待，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReleaseMutex&lt;/code&gt; 进行释放。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mutex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mutex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// 等待一小段时间，假装正在使用公共资源。这里的一段代码在单个进程之内将无法重入。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;_mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReleaseMutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数中有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;initiallyOwned&lt;/code&gt; 参数，如果指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表示创建这个 Mutex 的线程拥有这个资源（不需要等待），当这个线程调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReleaseMutex&lt;/code&gt; 之后其他线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 才会生效。&lt;/p&gt;

&lt;p&gt;不过这种方式不能达到跨进程同步的效果，所以实际上本文并不会过多描述这种互斥方式。&lt;/p&gt;

&lt;h2 id=&quot;创建跨进程互斥的-mutex&quot;&gt;创建跨进程互斥的 Mutex&lt;/h2&gt;

&lt;p&gt;要创建跨进程互斥的 Mutex，必须要给 Mutex 指定名称。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;new Mutex(false, &quot;Walterlv.Mutex&quot;)&lt;/code&gt; 创建一个命名的互斥锁，以便进行跨进程的资源互斥访问。&lt;/p&gt;

&lt;p&gt;在使用这个构造函数重载的时候，第一个参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;initiallyOwned&lt;/code&gt; 建议的取值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。因为当你指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 时，说明你希望此线程是初始创建此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mutex&lt;/code&gt; 的线程，然而由于你是直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的，所以你实质上是无法得知你到底是不是第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 不断地尝试访问一段资源。这样，当多个进程运行的时候，可以很大概率模拟出现资源访问冲突。&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Mutex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 正在使用公共资源。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这里的一段代码将无法重入，即使是两个不同的进程。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\lvyi\Desktop\walterlv.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 开始写入文件……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 开始写入文件……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 写入文件完成。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 写入文件完成。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReleaseMutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意此程序在两个进程下的运行效果，明明我们等待使用资源的时间间隔只有 50 ms，但实际上等待时间是 1000 ms 左右。在关掉其中一个进程之后，间隔恢复到了 50 ms 左右。&lt;/p&gt;

&lt;p&gt;这说明 Mutex 的等待在这里起到了跨进程互斥的作用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-30-named-mutex-demo.gif&quot; alt=&quot;以上代码在两个进程下的运行结果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当你需要在是否是第一次创建出来的时候进行一些特殊处理，就使用带 &lt;code class=&quot;highlighter-rouge&quot;&gt;createdNew&lt;/code&gt; 参数的构造函数。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    private void UseResource()
    {
&lt;span class=&quot;gd&quot;&gt;--      var mutex = new Mutex(false, &quot;Walterlv.Mutex&quot;);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++      var mutex = new Mutex(true, &quot;Walterlv.Mutex&quot;, out var createdNew);
&lt;/span&gt;
--      mutex.WaitOne();
&lt;span class=&quot;gi&quot;&gt;++      // 如果这个 Mutex 是由此处创建出来的，即 createdNew 为 true，说明第一个参数 initiallyOwned 是真的发生了，于是我们就不需要等待。
++      // 反之，当 createdNew 为 false 的时候，说明已经有一个现成的 Mutex 已经存在，我们在这里需要等待。
++      if (!createdNew)
++      {
++          mutex.WaitOne();
++      }
&lt;/span&gt;        ……
        mutex.ReleaseMutex();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;处理异常情况&quot;&gt;处理异常情况&lt;/h2&gt;

&lt;h3 id=&quot;applicationexception&quot;&gt;ApplicationException&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mutex.ReleaseMutex();&lt;/code&gt; 方法只能被当前拥有它的线程调用，如果某个线程试图调用这个函数，却没有拥有这个 Mutex，就会抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;怎样为拥有呢？还记得前面构造函数中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;initiallyOwned&lt;/code&gt; 参数吗？就是在指定自己是否是此 Mutex 的拥有者的（实际上我们还需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;createdNew&lt;/code&gt; 来辅助验证这一点）。&lt;/p&gt;

&lt;p&gt;当一个线程没有拥有这个 Mutex 的时候，需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 来等待获得这个锁。&lt;/p&gt;

&lt;h3 id=&quot;abandonedmutexexception&quot;&gt;AbandonedMutexException&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 开启一个线程，在那个线程中丢掉获得的 Mutex。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbandonMutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 不要让进程退出，否则 Mutex 就会被系统回收。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AbandonMutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 获得一个 Mutex，然后就不再释放了。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 由于此线程会在 WaitOne 执行结束后退出，所以这个 Mutex 就被丢掉了。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Mutex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的这段代码，当你第一次运行此进程并且保持此进程不退出的时候并没有什么异样。但是你再启动第二个进程实例的话，就会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 那里收到一个异常 —— &lt;code class=&quot;highlighter-rouge&quot;&gt;AbandonedMutexException&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以如果你不能在一处代码中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;try-finally&lt;/code&gt; 来确保在获得锁之后一定会释放的话，那么强烈建议在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 的时候捕获异常。顺便提醒，&lt;code class=&quot;highlighter-rouge&quot;&gt;try-finally&lt;/code&gt; 中不能有异步代码，你可以参见：&lt;a href=&quot;/post/deadlock-if-await-in-ui-lock-context&quot;&gt;在有 UI 线程参与的同步锁（如 AutoResetEvent）内部使用 await 可能导致死锁&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;也就是说，当你需要等待的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 一下异常。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 完之后，你并不需要再次使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 来等待，因为即便发生了异常，你也依然获得了锁。这一点你可以通过调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReleaseMutex&lt;/code&gt; 来验证，因为前面我们说了只有拥有锁的线程才可以释放锁。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Mutex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbandonedMutexException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;发现被遗弃的锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;获得了锁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/mutexes&quot;&gt;Mutexes - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex.-ctor&quot;&gt;Mutex Constructor (System.Threading) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex&quot;&gt;Mutex Class (System.Threading) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 30 Dec 2018 08:41:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/mutex-in-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/mutex-in-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>谨慎使用 FileInfo.Exists 实例方法，而是使用 File.Exists 静态方法替代</title>
        <description>&lt;p&gt;如果你在代码中使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 实例方法来判断一个文件是否存在，也许会发现此方法可能错误地判断来一个文件是否真的存在。这是一个坑。&lt;/p&gt;

&lt;p&gt;本文将介绍坑的原因，并提供填坑的办法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题代码&quot;&gt;问题代码&lt;/h2&gt;

&lt;p&gt;我们使用两种不同的方式判断文件是否存在：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 实例方法&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 静态方法&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\lvyi\Desktop\walterlv.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;FileInfo.Exists = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;    File.Exists = &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;----&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在运行这个程序，我们会发现，中途删除了 walterlv.log 文件之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 依然返回了 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 已经开始返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-30-named-mutex-demo.gif&quot; alt=&quot;以上代码在的运行结果&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;原因分析&quot;&gt;原因分析&lt;/h2&gt;

&lt;p&gt;实际翻阅代码可以发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 方法最终都是使用相同的方法来完成文件存在与否的判断。&lt;/p&gt;

&lt;p&gt;这是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 的判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exists&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_dataInitialised&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_dataInitialised&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 的最终判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileExists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIN32_FILE_ATTRIBUTE_DATA&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WIN32_FILE_ATTRIBUTE_DATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FillAttributeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnErrorOnNotFound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dwFileAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dwFileAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FILE_ATTRIBUTE_DIRECTORY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只不过，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 只会在没有初始化的时候初始化一次，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 是没有缓存的，每次都是直接去获取文件的属性（这就涉及到 IO）。&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;所以，如果你正在处理的文件在不同的时间可能存在也可能不存在，那么最好使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists&lt;/code&gt; 来判断文件存在与否，而不是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo.Exists&lt;/code&gt; 来判断。&lt;/p&gt;

&lt;p&gt;不过，如果你需要一次性判断文件的非常多的信息（而不只是文件存在与否），那么依然建议使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo&lt;/code&gt;，只不过在使用之前需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Refresh&lt;/code&gt; 进行一次刷新。&lt;/p&gt;
</description>
        <pubDate>Sun, 30 Dec 2018 08:41:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/file-exists-vs-fileinfo-exists.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/file-exists-vs-fileinfo-exists.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>C#/.NET 使用 CommandLineParser 来标准化地解析命令行</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLineParser&lt;/code&gt; 是一款用于解析命令行参数的 NuGet 包。你只需要关注你的业务，而命令行解析只需要极少量的配置代码。&lt;/p&gt;

&lt;p&gt;本文将介绍如何使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CommandLineParser&lt;/code&gt; 高效写出自己程序的命令行解析部分。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;nuget-包和-github-开源仓库&quot;&gt;NuGet 包和 GitHub 开源仓库&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;NuGet 包：&lt;a href=&quot;https://www.nuget.org/packages/CommandLineParser/&quot;&gt;CommandLineParser&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;GitHub 开源仓库：&lt;a href=&quot;https://github.com/commandlineparser/commandline&quot;&gt;commandlineparser/commandline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;最简单的命令行解析&quot;&gt;最简单的命令行解析&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'f'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;需要处理的文件。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Files&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'o'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;override&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;是否覆盖原有文件。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Override&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParseArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithParsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 使用解析后的命令行参数进行操作。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Override&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;覆盖&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;使用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;walterlv 正在&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;文件 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个简单的 Demo 程序使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 类来封装命令行参数，&lt;code class=&quot;highlighter-rouge&quot;&gt;Parser.Default.ParseArguments&lt;/code&gt; 解析到的参数将存入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 类型的实例中。而只需要加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;WithParsed&lt;/code&gt; 即可在一个新的方法中使用我们解析后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 实例。&lt;/p&gt;

&lt;p&gt;这时，在命令行中就可以使用命令了：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;demo.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\Test.txt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-22-56-11.png&quot; alt=&quot;在命令行中使用命令&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于我们标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;Files&lt;/code&gt; 是必要属性，所以如果此参数没有指定，将返回命令行的使用说明。此使用说明中就包含了我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&lt;/code&gt; 参数中编写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HelpText&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Options&lt;/code&gt; 类中单次是多单词的短语，那么建议在指定名称的时候为每一个单词之间添加一个空格。这样参数就不会让多个单词连成一片难以辨认。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;long-name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;需要处理的文件。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LongName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么命令是：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;demo.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--long-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;xxx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果不指定，那么就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;--longname&lt;/code&gt;，这显然不好看。&lt;/p&gt;

&lt;h2 id=&quot;包含多个方法的命令行解析&quot;&gt;包含多个方法的命令行解析&lt;/h2&gt;

&lt;p&gt;如果一个命令行程序只做一件事情，那么以上代码足以应付大多数的情况。可是有时候一个命令行程序是为了做一类事情的 —— 典型的例子就是 git 程序。当你运行 git 的时候，你可以在 git 后面加一个谓词（动词），表示执行的是哪一个命令。后面的参数是每个命令都不同的，并且第一个参数是不用指定名称的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;check&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;检查&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CheckOptions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;一个 .sln 文件，一个或者多个 .csproj 文件。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputFiles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;修复&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FixOptions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;一个 .sln 文件，一个或者多个 .csproj 文件。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputFiles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'o'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;outputFiles&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;修复之后的文件集合。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutputFiles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelpText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;是否自动决定版本号，这将使用冲突版本号中的最新版本。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exitCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParseArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CheckOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CheckOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckSolutionOrProjectFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FixOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixSolutionOrProjectFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exitCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckSolutionOrProjectFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CheckOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixSolutionOrProjectFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FixOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于这一段程序，我们可以使用两种不同的谓词来执行命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;demo.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\Test\Test.csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;demo.dll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\Test\Test.csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi\Desktop\TestFix\Test.csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;verboption-和-value&quot;&gt;Verb，Option 和 Value&lt;/h2&gt;

&lt;p&gt;Verb 是在一个命令行选项的 Option 类上标记的，用于指定命令的类别。每一个 Verb 标记的类别都可以有自己独立的一套命令行参数。&lt;/p&gt;

&lt;p&gt;Option 是命名的命令行参数。在命令行中，你必须指定命令行缩写或者全称来指定命令行参数的不同类型。&lt;/p&gt;

&lt;p&gt;Value 是命令行的无名参数，它是靠在命令行谓词后面的参数位置来确定解析到哪一个属性上的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/commandlineparser/commandline&quot;&gt;commandlineparser/commandline: The best C# command line parser that brings standardized *nix getopt style, for .NET. Includes F# support&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/commandlineparser/commandline/wiki&quot;&gt;Home · commandlineparser/commandline Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%A7%A3%E6%9E%90%E5%B7%A5%E5%85%B7.html&quot;&gt;C＃命令行解析工具 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2017/07/18/the-week-in-net-command-line-parser-library-net-south-east/&quot;&gt;The week in .NET – Command Line Parser Library, .NET South East - .NET Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 30 Dec 2018 08:10:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/introduce-command-line-parser.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/introduce-command-line-parser.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>只需 5 秒钟，你就能取到 WPF 程序的超高分辨率超高清截图</title>
        <description>&lt;p&gt;我想要截取一个 WPF 程序的图标，但是它太小了。如果我就这样截屏截下来，是很不高清的。由于我需要制作一份课件，所以我需要超高清版本，可是，如何做才能最快速拿到 WPF 程序的超高清截图呢？&lt;/p&gt;

&lt;p&gt;本文分享一个方法，只需 5 秒钟，你就能拿到！&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;第一步打开-snoop&quot;&gt;第一步：打开 Snoop&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-26-12-01-34.png&quot; alt=&quot;Snoop 的界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你没有 Snoop，请前往下载：&lt;a href=&quot;https://github.com/cplotts/snoopwpf/releases/tag/2.10.0&quot;&gt;Release Snoop 2.10.0 · cplotts/snoopwpf&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;第二步使用-snoop-的放大功能&quot;&gt;第二步：使用 Snoop 的放大功能&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-26-12-03-28.png&quot; alt=&quot;放大功能&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将你的鼠标放到两个瞄准按钮的左边那个上面，你可以看到提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Drag and drop this crosshairs over a WPF window in order to Magnify it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，你将这个按钮拖拽到你希望放大的 WPF 程序上面，松开鼠标。&lt;/p&gt;

&lt;p&gt;这时，会弹出一个新的窗口出来，将其最大化：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-26-12-05-03.png&quot; alt=&quot;需要最大化&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你就能看到 WPF 应用的超高清版本了。使用鼠标滚轮可以继续放大或缩小。&lt;/p&gt;

&lt;h2 id=&quot;查看效果&quot;&gt;查看效果&lt;/h2&gt;

&lt;p&gt;比如一个按钮，现在可以放到这么大来看了。截图可以截出高清版本。&lt;/p&gt;

&lt;p&gt;实际上前面的 Snoop 界面也是这样放大的，注意到了吗？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-26-12-50-52.png&quot; alt=&quot;放大之后的同步按钮&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 26 Dec 2018 04:54:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-get-high-resolution-view-of-a-wpf-app.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-get-high-resolution-view-of-a-wpf-app.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Write a MSBuild Target to detect whether the project is rebuilding or not</title>
        <description>&lt;p&gt;MSBuild or the dotnet build command both supports Incremental Building for compiling performance. You can read &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-build-incrementally&quot;&gt;How to: Build Incrementally - Visual Studio - Microsoft Docs&lt;/a&gt; to lean more about incremental building. When a target supports increment building and the project is rebuilding for the moment, the Target will not execute. So if it affects followed other Targets, it cannot be set to incremental building.&lt;/p&gt;

&lt;p&gt;But how can I detect a incremental building behavior and do something different things if my Target affects followed other Targets? In this post, I’ll talk about that.&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;SourceFusion is a pre-compile framework and allows you to change you code during the compiling. You can visit &lt;a href=&quot;https://github.com/dotnet-campus/SourceFusion&quot;&gt;dotnet-campus/SourceFusion: SourceFusion is a pre-compile framework based on Roslyn. It helps you to build high-performance .NET code.&lt;/a&gt; to view the open-source project.&lt;/p&gt;

&lt;p&gt;The Target in the SourceFusion takes long time and affects followed Targets such as the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; Target. If it use a completely incremental building, the Target will be skipped when building and no more source code will be added or removed before the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; Target. So nothing will happen during a incremental building and the SourceFusion changes nothing.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;p&gt;We can write another Target helps us to detect rebuilding behavior. We can define a property to tell us whether it is a incremental building or not.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTest&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoCoreTarget&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Inputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildProjectFullPath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Outputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemoFolder)RebuildingTest.txt&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RebuildingTestLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;CallTarget&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTestInitialize&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;WriteLinesToFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemoFolder)RebuildingTest.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Lines=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(RebuildingTestLine)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Overwrite=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTestInitialize&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvDemoRebuildRequired&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionRebuildRequired&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this code, I write two Targets and the second one doesn’t define any &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; attributes. So this Target will not be executed automatically unless you call it explicitly. I define a property named &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionRebuildRequired&lt;/code&gt; in it to flag the &lt;em&gt;rebuilding&lt;/em&gt; status.&lt;/p&gt;

&lt;p&gt;I call this separated Target in the first target which defines &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; attributes. We can know that if a Target want to support incremental building the two attributes are important. So this Target supports that.&lt;/p&gt;

&lt;p&gt;These are the three mentioned Targets:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTest&lt;/code&gt; The Target that supports the incremental building&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; The Target to assign a value to property &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceFusionRebuildRequired&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoCoreTarget&lt;/code&gt; The long-time Target that will use the incremental building test value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use a csproj file as a input file and another temp file as a output file. Then if a project file changed and a rebuilding will happen. To generate a temp output file I should use &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteLinesToFile&lt;/code&gt; Task to write one.&lt;/p&gt;

&lt;p&gt;I need &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; property to detect the rebuilding behavior. If the project is rebuilding the property will be assigned because the &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; is called and if the project is incremental building the property will not be assigned.&lt;/p&gt;

&lt;p&gt;Then we can check the value of &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; to detect a rebuilding or incremental building.&lt;/p&gt;

&lt;h2 id=&quot;how-to-use-this-property&quot;&gt;How to use this property&lt;/h2&gt;

&lt;p&gt;For the long-time Target &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoCoreTarget&lt;/code&gt;, it should detect the property and do something different.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoCoreTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvDemoRebuildRequired&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(WalterlvDemoRebuildRequired)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvDemoRebuildRequired&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ConsoleToMSBuild=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo.exe -r $(WalterlvDemoRebuildRequired)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We define the same property only if it is not been assigned. But we assign it as a &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; value which is different to the &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; Target.&lt;/p&gt;

&lt;p&gt;Then pass the property value to the core Task, and the Task will know whether it is completely rebuilding or incremental building.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/calltarget-task&quot;&gt;CallTarget Task - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-build-incrementally&quot;&gt;How to: Build Incrementally - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions&quot;&gt;Property Functions - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 25 Dec 2018 00:07:34 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detecting-rebuild-switch-using-msbuild-target-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detecting-rebuild-switch-using-msbuild-target-en.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>编写 Target 检测 MSBuild / dotnet build 此次编译是否是差量编译</title>
        <description>&lt;p&gt;MSBuild 或 Roslyn 编译项目时均支持差量编译，毕竟为了性能。我在 &lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译&lt;/a&gt; 一文中介绍了如何使一个 Target 支持差量编译。在那篇文章中我说到差量编译会导致 Target 不执行；也就是说，如果一个 Target 对后续的编译会产生影响，那么一定不能设置为差量编译。&lt;/p&gt;

&lt;p&gt;不过，真的会写出一些非常耗时的 Target，但是它会对后续的编译产生影响。这些 Target 如果要做差量编译，那么就不能直接使用原生的差量编译方案了。本文将介绍如何处理这样的情况。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;我们遇到的问题&quot;&gt;我们遇到的问题&lt;/h2&gt;

&lt;p&gt;SourceFusion 是一个预编译框架，它在你编译期间对你的代码做一些改变。&lt;a href=&quot;https://github.com/dotnet-campus/SourceFusion&quot;&gt;dotnet-campus/SourceFusion: SourceFusion is a pre-compile framework based on Roslyn. It helps you to build high-performance .NET code.&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这意味着，这个耗时的 Target 是会改变后续的编译的，典型的是 —— 它会在编译期间增加和删除几个源代码文件。如果完全使用 Target 原生的差量编译，那么一旦这个 Target 跳过，那么也就不会增加和删除任何源代码文件了。&lt;/p&gt;

&lt;h2 id=&quot;解决方案&quot;&gt;解决方案&lt;/h2&gt;

&lt;p&gt;解决方案是，我们写一个前置的 Target，这个 Target 支持差量编译。于是我们可以利用它的差量编译特性得知当前是否处于差量编译的状态。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTest&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoCoreTarget&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Inputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildProjectFullPath)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Outputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemoFolder)RebuildingTest.txt&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RebuildingTestLine&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;CallTarget&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Targets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTestInitialize&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;WriteLinesToFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;File=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(WalterlvDemoFolder)RebuildingTest.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Lines=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(RebuildingTestLine)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Overwrite=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_WalterlvDemoRebuildingTestInitialize&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvDemoRebuildRequired&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SourceFusionRebuildRequired&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后再写一个新的 Target。这个新的 Target 没有任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 的设置。也就是说，如果没有显式地去执行它或者将它设置为默认的 Target，它将完全不会执行。而我们在这个 Target 里面会设置一个属性，标记此时正在处于“重新编译”的状态（即不是差量状态）。&lt;/p&gt;

&lt;p&gt;我们使用那个支持差量编译的 Target，通过 CallTarget 来显式调用这个新的 Target。如果当前处于差量状态，那么这个 CallTarget 不会执行；而如果处于全量编译状态，那么 CallTarget 就会调用那个新的 Target 以便设置一个属性。&lt;/p&gt;

&lt;p&gt;上面写了两个 Target，但涉及到三个 Target：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTest&lt;/code&gt; 是我给这个差量编译测试 Target 取的名字&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; 是差量编译初始化赋值的 Target&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoCoreTarget&lt;/code&gt; 是那个耗时的 Target。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;根据我在 &lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译&lt;/a&gt; 一文中的差量编译的做法，我使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(MSBuildProjectFullPath)&lt;/code&gt; 也就是 csproj 文件的改变来决定差量检测的输入，用一个临时的文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;RebuildingTest.txt&lt;/code&gt; 来决定差量编译的输出。在这里，我们一定需要一个文件来输出，这样 MSBuild 或者 Roslyn 检测差量的时候才能正确完成。这样，为了得到这个文件，我们实际上需要通过这个 Target 真的写一个文件出来，所以我们用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteLinesToFile&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;实际上，我们真正需要的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; 这个属性，而这个属性我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; 中进行设置。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTest&lt;/code&gt; 中，只有全量编译时才会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTestInitialize&lt;/code&gt; 而差量编译是不会调用的。所以差量编译时，&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; 不会初始化。&lt;/p&gt;

&lt;p&gt;这样，我们便可以通过这个属性判断是否设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 来得知当前是否处于全量编译状态。&lt;/p&gt;

&lt;h2 id=&quot;后续使用&quot;&gt;后续使用&lt;/h2&gt;

&lt;p&gt;对于我们真实的耗时的 Target，则需要检测这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; 的值，进行不同的处理。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoCoreTarget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WalterlvDemoRebuildRequired&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'$(WalterlvDemoRebuildRequired)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WalterlvDemoRebuildRequired&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ConsoleToMSBuild=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo.exe -r $(WalterlvDemoRebuildRequired)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在核心的 Target 里面判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoRebuildRequired&lt;/code&gt; 的值，如果没有被设置，说明前面的 Target 没有执行，也就是“被差量”了，我们就可以将之指定为 false。&lt;/p&gt;

&lt;p&gt;这样，核心的 Target 里面，也就是 WalterlvDemo.exe 执行参数中，就可以拿到正确的差量状态了。&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表示正在重新编译，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 表示正在差量编译。&lt;/p&gt;

&lt;h2 id=&quot;一些坑&quot;&gt;一些坑&lt;/h2&gt;

&lt;p&gt;如果不写那个新的 Target 是否可行呢？我们能否把这个属性的赋值直接放到差量编译的那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_WalterlvDemoRebuildingTest&lt;/code&gt; 中？&lt;/p&gt;

&lt;p&gt;其实这是不靠谱的。&lt;/p&gt;

&lt;p&gt;MSBuild 在计算属性的时候，不同的 csproj 格式、不同版本的计算情况不同。实际上在不断的试验中我并没有找到哪些情况下差量 Target 的属性会被计算哪些情况不会被计算。所以最好的办法是 —— 不要依赖于这些不确定的属性变化。&lt;/p&gt;

&lt;p&gt;所以我们写一个新的 Target，Target 执行则属性赋值，不执行则不赋值，非常确定。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/calltarget-task&quot;&gt;CallTarget Task - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-build-incrementally&quot;&gt;How to: Build Incrementally - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions&quot;&gt;Property Functions - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 24 Dec 2018 23:43:28 +0000</pubDate>
        <link>https://blog.walterlv.com/post/detecting-rebuild-switch-using-msbuild-target.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/detecting-rebuild-switch-using-msbuild-target.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>在 Roslyn 分析语法树时添加条件编译符号的支持</title>
        <description>&lt;p&gt;我们在代码中会写 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Conditional(&quot;DEBUG&quot;)]&lt;/code&gt; 来使用已经定义好的条件编译符号。而定义条件编译符号可以在代码中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;#define WALTERLV&lt;/code&gt; 来实现，也可以通过在项目属性中设置条件编译符号（Conditional Compilation Symbols）来实现。&lt;/p&gt;

&lt;p&gt;然而如果我们没有做任何特殊处理，那么使用 Roslyn 分析使用了条件编译符号的源码时，就会无法识别这些源码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你不知道条件编译符号是什么或者不知道怎么设置，请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/how-to-define-preprocessor-symbols&quot;&gt;.NET/C# 项目如何优雅地设置条件编译符号？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;我们在使用 Roslyn 分析语法树时，会创建语法树的一个实例。如果使用默认的构造函数，那么就不会识别设置了条件编译符号的语句，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-24-22-29-21.png&quot; alt=&quot;不识别条件编译符号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而实际上构造函数的参数中带有 &lt;code class=&quot;highlighter-rouge&quot;&gt;preprocessorSymbols&lt;/code&gt; 参数，即预处理符号。在传入此预处理符号的情况下，Roslyn 就可以识别此符号了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-24-22-33-05.png&quot; alt=&quot;识别的条件编译符号&quot; /&gt;&lt;/p&gt;

&lt;p&gt;方法是传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;preprocessorSymbols&lt;/code&gt; 参数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preprocessorSymbols&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DEBUG&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TRACE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;WALTERLV&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;NETCOREAPP2_1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CSharpParseOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LanguageVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Latest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DocumentationMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SourceCodeKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Regular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preprocessorSymbols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此后，你可以拿 &lt;code class=&quot;highlighter-rouge&quot;&gt;syntaxTree&lt;/code&gt; 做其他事情了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compileTypeVisitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompileTypeVisitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;compileTypeVisitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Types&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compileTypeVisitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Types&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然这段代码你可能编译不通过，因为这是另一篇博客中的源码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文所用的查看语法树的插件，你可以查看另一篇博客：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 24 Dec 2018 14:36:28 +0000</pubDate>
        <link>https://blog.walterlv.com/post/roslyn-syntax-tree-supporting-preprocessor-symbols.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/roslyn-syntax-tree-supporting-preprocessor-symbols.html</guid>
        
        
        <category>roslyn</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>.NET 中什么样的类是可使用 await 异步等待的？</title>
        <description>&lt;p&gt;我们已经知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 是可等待的，但是去看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 类的实现，几乎找不到哪个基类、接口或者方法属性能够告诉我们与 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 相关。&lt;/p&gt;

&lt;p&gt;而本文将探索什么样的类是可使用 await 异步等待的？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern&quot;&gt;Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern&lt;/a&gt; 一文解决了我们的疑惑。&lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 是给编译器用的，只要我们的类包含一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 方法，并返回合适的对象，我们就能让这个类的实例被 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 使用了。&lt;/p&gt;

&lt;p&gt;既然需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 方法，那我们先随便写个方法探索一下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尝试调用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译器告诉我们：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test.GetAwaiter() 不可访问，因为它具有一定的保护级别。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;原来 GetAwaiter 方法需要是可以被调用方访问到的才行。&lt;/p&gt;

&lt;p&gt;于是我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 前面的访问修饰符改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt;。现在提示变成了：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;await 要求类型 Test 包含适当的 GetAwaiter 方法。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;考虑到一定要获取到某个对象才可能有用，于是我们返回一个 Test2 对象：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Test2&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时编译器又告诉我们：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test2 未包含 IsCompleted 的定义。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;public bool IsCompleted { get; }&lt;/code&gt;，编译器又说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test2 不实现 INotifyCompletion。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是我们实现之，编译器又告诉我们：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test2 未包含 GetResult 的定义。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是我们加上一个空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetResult&lt;/code&gt; 方法，现在编译器终于不报错了。&lt;/p&gt;

&lt;p&gt;现在我们一开始的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoAsync&lt;/code&gt; 和辅助类型变成了这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注：此处为试验代码。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Test2&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;总结起来，要想使一个方法可被 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等待，必须具备以下条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这个方法返回一个类 A 的实例，这个类 A 必须满足后面的条件。&lt;/li&gt;
  &lt;li&gt;此类 A 有一个可被访问到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 方法（扩展方法也行，这算是黑科技吗？），方法返回类 B 的实例，这个类 B 必须满足后面的条件；&lt;/li&gt;
  &lt;li&gt;此类 B 实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;INotifyCompletion&lt;/code&gt; 接口，且拥有 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool IsCompleted { get; }&lt;/code&gt; 属性、&lt;code class=&quot;highlighter-rouge&quot;&gt;GetResult()&lt;/code&gt; 方法、&lt;code class=&quot;highlighter-rouge&quot;&gt;void OnCompleted(Action continuation)&lt;/code&gt; 方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;更多编写自定义 Awaiter 的文章可以阅读：&lt;/p&gt;

&lt;p&gt;入门篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实战篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern&quot;&gt;Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 07:17:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/what-is-an-awaiter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/what-is-an-awaiter.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？</title>
        <description>&lt;p&gt;.NET 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 写异步代码用起来真的很爽，就像写同步一样。我们可以在各种各样的异步代码中看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 返回值，这样大家便可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等待这个方法。不过，有时需要写一些特别的异步方法，这时需要自己来实现一个可以异步等待的对象。&lt;/p&gt;

&lt;p&gt;本文将讲述如何实现一个可等待对象，一个自定义的 Awaiter。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;awaiter-系列文章&quot;&gt;Awaiter 系列文章&lt;/h2&gt;

&lt;p&gt;入门篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实战篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;可等待对象&quot;&gt;可等待对象&lt;/h2&gt;

&lt;p&gt;我们希望大家在调用下面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CallWalterlvAsync&lt;/code&gt; 方法的时候，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字来异步等待：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 返回一个 WalterlvOperation，以便外面调用方可以异步等待。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以我们需要实现一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvOperation&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;编写基本的-awaiter-框架代码&quot;&gt;编写基本的 Awaiter 框架代码&lt;/h2&gt;

&lt;p&gt;先写一个空的类型，然后为它编写一个空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 方法，返回新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvAwaiter&lt;/code&gt; 类型。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 委托 walterlv 来完成一项特殊的任务。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 通过在代码当中调用，可以让他在现实中为你做一些事情。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvOperation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接着，我们编写 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvAwaiter&lt;/code&gt; 类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;必须实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;INotifyCompletion&lt;/code&gt; 接口，此接口带来了 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 方法。另外两个方法不是接口带来的，但是也是实现一个自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt; 必要的方法。&lt;/p&gt;

&lt;p&gt;在你编写完以上两段代码之后，你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 就可以编译通过了。&lt;/p&gt;

&lt;p&gt;额外说明一下，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetResult&lt;/code&gt; 方法是可以修改返回值的，只要返回值不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;void&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等待的地方将可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 完成之后获得一个返回值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 于是你可以拿到一个字符串类型的返回值。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;写博客&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;实现基本的-awaiter&quot;&gt;实现基本的 Awaiter&lt;/h2&gt;

&lt;p&gt;以上代码只能编译通过，但实际上如果你跑起来，会发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 一旦进入，是不会再往下执行的。因为我们还没有实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvAwaiter&lt;/code&gt; 类型。&lt;/p&gt;

&lt;p&gt;最重要的，是需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 方法传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;continuation&lt;/code&gt; 委托。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;像以上这么写之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 之后的代码便可以执行了。&lt;/p&gt;

&lt;p&gt;如果你只是希望了解如何实现一个 Awaiter，那么写出以上的代码就足以。因为这才是最本质最核心的 Awaiter 的实现。&lt;/p&gt;

&lt;p&gt;不过，以上代码的执行是立即执行，没有任何异步的效果。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 被调用的时候，我们立刻调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;continuation&lt;/code&gt; 的执行。&lt;/p&gt;

&lt;h2 id=&quot;实现异步的-awaiter&quot;&gt;实现异步的 Awaiter&lt;/h2&gt;

&lt;p&gt;要真正达到异步的效果，&lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 执行的时候，我们不能立刻去调用参数传进来的委托，而只是将他记录下来，等到任务真正完成的时候再去调用。&lt;/p&gt;

&lt;p&gt;以下的代码就不再是通用的代码了，你需要针对你的不同业务去设计如何异步完成一个任务，然后再通知到异步等待的代码继续执行。&lt;/p&gt;

&lt;p&gt;例如，现在我们期望 walterlv 代理去写博客，于是我们为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvOperation&lt;/code&gt; 加一点功能，真正去做一些异步的事情。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CallWalterlvAsync&lt;/code&gt; 的实现现在真的开启了一个异步操作。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后为了实现我们自己添加的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Start&lt;/code&gt; 方法，我们在里面去做一些事情。里面第一句就离开了当前线程前往线程池中的其他线程去执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine&lt;/code&gt; 了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 委托 walterlv 来完成一项特殊的任务。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 通过在代码当中调用，可以让他在现实中为你做一些事情。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvOperation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_task&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_awaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;walterlv 已经收到任务：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;开始执行&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;walterlv 已经完成 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReportCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 返回一个可等待对象，以便能够使用 await 关键字进行异步等待。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是现在可以通过下面的代码来要求 walterlv 去写博客了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;写博客&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而实际上，我们上面还留了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;_awaiter.ReportCompleted&lt;/code&gt; 方法没有实现。由于我们的操作全部是异步的了，这个方法的实现就是为了通知所有正在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 等待的代码，异步任务完成了，可以继续往后面执行了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这个函数我们暂时还没有真正实现，因为需要进行同步等待比较复杂。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 我们将在本文后面附的其他博客中实现。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 当这个 Awaiter 被 await 等待的时候，此代码会被调用。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 每有一处 await 执行到，这里就会执行一次，所以在任务完成之前我们需要 +=。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReportCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 由 WalterlvOperation 来通知这个任务已经完成。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在运行程序，会按照异步任务来执行，可以异步等待：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CallWalterlvAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;写博客&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-13-40-55.png&quot; alt=&quot;程序运行结果&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 07:17:09 +0000</pubDate>
        <link>https://blog.walterlv.com/post/understand-and-write-custom-awaiter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/understand-and-write-custom-awaiter.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便</title>
        <description>&lt;p&gt;我在几篇文章中都说到了在 .NET 中自己实现 Awaiter 情况。&lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 写异步代码用起来真的很爽，就像写同步一样。然而实现 Awaiter 没有现成的接口，它需要你按照编译器的要求为你的类型添加一些具有特定名称的属性和方法。然而没有接口的帮助，我们编写起来就很难获得工具（如 ReSharper）自动生成代码的支持。&lt;/p&gt;

&lt;p&gt;本文将分享我提取的自己实现 Awaiter 的接口。你只需要实现这些接口当中的 2 个，就能正确实现一个 Awaitable 和 Awaiter。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;接口代码&quot;&gt;接口代码&lt;/h2&gt;

&lt;p&gt;你可以在 GitHub 上找到这段代码：&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Threading/AwaiterInterfaces.cs&quot;&gt;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Threading/AwaiterInterfaces.cs&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IAwaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IAwaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TAwaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ICriticalAwaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICriticalNotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ICriticalAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICriticalNotifyCompletion&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;接口实现&quot;&gt;接口实现&lt;/h2&gt;

&lt;p&gt;在 ReSharper 工具的帮助下，你可以在继承接口之后快速编写出实现代码来：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-14-05-42.png&quot; alt=&quot;使用 ReSharper 快速实现 Awaiter&quot; /&gt;&lt;br /&gt;
▲ 使用 ReSharper 快速实现 Awaiter&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-14-09-23.png&quot; alt=&quot;使用 ReSharper 快速实现 Awaitable&quot; /&gt;&lt;br /&gt;
▲ 使用 ReSharper 快速实现 Awaitable&lt;/p&gt;

&lt;p&gt;于是我们可以迅速得到一对可以编译通过的 Awaitable 和 Awaiter：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Awaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Awaitable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Awaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，你也可以在一个类里面实现这两个接口：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Awaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Awaiter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;实现业务需求&quot;&gt;实现业务需求&lt;/h2&gt;

&lt;p&gt;我有另外两篇文章在实现真正可用的 Awaiter：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多-awaiter-系列文章&quot;&gt;更多 Awaiter 系列文章&lt;/h2&gt;

&lt;p&gt;入门篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实战篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 07:17:04 +0000</pubDate>
        <link>https://blog.walterlv.com/post/abstract-awaitable-and-awaiter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/abstract-awaitable-and-awaiter.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>了解 .NET 的默认 TaskScheduler 和线程池（ThreadPool）设置，避免让 Task.Run 的性能急剧降低</title>
        <description>&lt;p&gt;.NET Framework 4.5 开始引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt;，它可以很方便的帮助我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 语法，同时还使用线程池来帮助我们管理线程。以至于我们编写异步代码可以像编写同步代码一样方便。&lt;/p&gt;

&lt;p&gt;不过，如果滥用，也可能导致应用的性能急剧下降。本文将说明在默认线程池配置（&lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPoolTaskScheduler&lt;/code&gt;）的情况下，应该如何使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 来避免性能的急剧降低。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何使用-taskrun&quot;&gt;如何使用 Task.Run？&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;对于 IO 操作，尽量使用原生提供的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Async&lt;/code&gt; 方法（不要自己使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 调用一个同步的版本占用线程池资源）；&lt;/li&gt;
  &lt;li&gt;对于没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Async&lt;/code&gt; 版本的 IO 操作，如果可能耗时很长，则指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateOptions&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;LongRunning&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;其他短时间执行的任务才推荐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;接下来分析原因：&lt;/p&gt;

&lt;h2 id=&quot;示例程序和示例代码&quot;&gt;示例程序和示例代码&lt;/h2&gt;

&lt;p&gt;在开始之前，我们先准备一个测试程序。这个程序一开始就使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 跑起来 10 个异步任务，每一个里面都等待 5 秒。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-23-14-53-33.png&quot; alt=&quot;使用 Task.Run 跑起来的异步代码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以发现，虽然我们是同一时间启动的 10 个异步任务，但任务的实际开始时间并不相同 —— 前面 8 个任务立刻开始了，而后面每隔一秒才会启动一个新的异步任务。&lt;/p&gt;

&lt;p&gt;示例程序的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv task demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LongTimeTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LongTimeTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManagedThreadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 异步任务已开始……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 这一句才是关键，等待。其他代码只是为了输出。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForegroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] 异步任务已结束……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForegroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;taskscheduler&quot;&gt;TaskScheduler&lt;/h2&gt;

&lt;p&gt;造成以上异步任务不马上开始的原因，与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskScheduler&lt;/code&gt; 有关。默认情况下，&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 使用的是 .NET 提供的默认 Scheduler，可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskScheduler.Default&lt;/code&gt; 获取到。&lt;/p&gt;

&lt;p&gt;Task 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskScheduler&lt;/code&gt; 来决定何时执行一个异步任务，如果你不设置，默认的实现是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPoolTaskScheduler&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你可以前往 .NET Core 的源码页面查看源码：&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs,33cd274e06874569,references&quot;&gt;ThreadPoolTaskScheduler.QueueTask&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;于是，你在线程池中的设置将决定一个 Task 将在何时开启一个线程执行。&lt;/p&gt;

&lt;h2 id=&quot;threadpool&quot;&gt;ThreadPool&lt;/h2&gt;

&lt;p&gt;通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPool.GetMinThreads&lt;/code&gt; 可以获得最小的线程数和异步 IO 完成线程数；通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPool.GetMaxThreads&lt;/code&gt; 来获得其最大值。通过对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;set&lt;/code&gt; 方法来设置最小值和最大值。&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool.getminthreads?wt.mc_id=MVP&quot;&gt;ThreadPool.GetMinThreads(Int32, Int32) Method (System.Threading) - Microsoft Docs&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;线程池按需提供新的工作线程或 I/O 完成线程直到它达到每个类别的最小值。&lt;/li&gt;
  &lt;li&gt;默认情况下，最小线程数设置为在系统上的处理器数。&lt;/li&gt;
  &lt;li&gt;当达到最小值时，线程池可以创建该类别中的其他线程或等待，直到一些任务完成。&lt;/li&gt;
  &lt;li&gt;需求较低时，线程池线程的实际数量可以低于最小值。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是便会出现我们在本文一开始运行时出现的结果图。在我的计算机上（八核），最小线程数是 8，于是开始的 8 个任务可以立即开始执行。当达到数量 8 而依然没有线程完成执行的时候，线程池会尝试等待任务完成。但是，1 秒后依然没有任务完成，于是线程池创建了一个新的线程来执行新的任务；接下来是每隔一秒会开启一个新的线程来执行现有任务。当有任务完成之后，就可以直接使用之前完成了任务的线程继续完成新的任务。&lt;/p&gt;

&lt;p&gt;不过，每个类别创建线程的总数量受到最大线程数限制。&lt;/p&gt;

&lt;h2 id=&quot;推荐的使用方法&quot;&gt;推荐的使用方法&lt;/h2&gt;

&lt;p&gt;了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPoolTaskScheduler&lt;/code&gt; 的默认行为之后，我们可以做这些事情来充分利用线程池带来的优势：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对于 IO 操作，尽量使用原生提供的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Async&lt;/code&gt; 方法，这些方法使用的是 IO 完成端口，占用线程池中的 IO 线程而不是普通线程（不要自己使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 占用线程池资源）；&lt;/li&gt;
  &lt;li&gt;对于没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Async&lt;/code&gt; 版本的 IO 操作，如果可能耗时很长，则指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateOptions&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;LongRunning&lt;/code&gt;（这样便会直接开一个新线程，而不是使用线程池）。&lt;/li&gt;
  &lt;li&gt;其他短时间执行的任务才推荐使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler?wt.mc_id=MVP&quot;&gt;TaskScheduler Class (System.Threading.Tasks) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?wt.mc_id=MVP&quot;&gt;TaskCreationOptions Enum (System.Threading.Tasks) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff963549(v=pandp.10)?wt.mc_id=MVP&quot;&gt;Parallel Tasks - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/attached-and-detached-child-tasks?wt.mc_id=MVP&quot;&gt;Attached and Detached Child Tasks - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool.getminthreads?wt.mc_id=MVP&quot;&gt;ThreadPool.GetMinThreads(Int32, Int32) Method (System.Threading) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/threading/managed-threading-best-practices?wt.mc_id=MVP&quot;&gt;Managed Threading Best Practices - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 07:16:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/default-task-scheduler-and-thread-pool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/default-task-scheduler-and-thread-pool.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Turn on Mobile Hotspot (Wi-Fi) in Windows 10</title>
        <description>&lt;p&gt;We’ll learn how to turn on Mobile Hotspot (Wi-Fi) in Windows 10.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/open-mobile-hotspot-in-windows-10.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/open-mobile-hotspot-in-windows-10-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;Go to Settings -&amp;gt; 网络和 Internet -&amp;gt; 移动热点&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-23-13-12-37.png&quot; alt=&quot;Turn on Wi-Fi&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Turn on &lt;em&gt;Share my Internet connection with other devices&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you want to change your Network name and Network password, just click &lt;em&gt;Edit&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You’ll find this hotspot in your other devices. After you connect your device to this hotspot you’ll soon find that all the devices are listed in the Devices connected section.&lt;/p&gt;

&lt;p&gt;Click the network icon in the task bar notification area and you can also turn on the Wi-Fi hotspot there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-23-13-34-09.png&quot; alt=&quot;The Network Icon&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 05:35:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/open-mobile-hotspot-in-windows-10-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/open-mobile-hotspot-in-windows-10-en.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>在 Windows 10 中开启移动 WLAN 热点</title>
        <description>&lt;p&gt;本文将介绍如何在 Windows 10 中开启移动 Wi-Fi 热点。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/open-mobile-hotspot-in-windows-10.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/open-mobile-hotspot-in-windows-10-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;要在 Windows 10 中开启移动 WLAN，需要进入设置 -&amp;gt; 网络和 Internet -&amp;gt; 移动热点&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-23-13-21-40.png&quot; alt=&quot;移动热点&quot; /&gt;&lt;/p&gt;

&lt;p&gt;开启“与其他设备共享我的 Internet 连接”。&lt;/p&gt;

&lt;p&gt;如果你需要修改网络名称和密码，点击下面的编辑即可。&lt;/p&gt;

&lt;p&gt;在开启之后，你可以在其他设备中发现这个新的热点，连接上之后就可以在下面“已连接的设备”中看到所有正在连接的设备。&lt;/p&gt;

&lt;p&gt;还有其他的开启方式，直接在任务栏的通知区域，打开网络图标，最右边就有移动热点的开关。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-23-13-27-43.png&quot; alt=&quot;通知区域的网络图标&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Dec 2018 05:27:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/open-mobile-hotspot-in-windows-10.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/open-mobile-hotspot-in-windows-10.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter</title>
        <description>&lt;p&gt;.NET 和 C# 共同给我们带来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步编程模型（TAP）用起来真的很爽。为了实现异步等待，我们只需要在一切能够能够异步等待的方法前面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 即可。能够异步等待的最常见的类型莫过于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;，但也有一些其他类型。即便有些耗时操作没有返回可等待的类型，我们也可以用一句 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run(action)&lt;/code&gt; 来包装（&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E5%BC%82%E6%AD%A5%E8%BD%AC%E5%90%8C%E6%AD%A5.html&quot;&gt;同步转异步 - 林德熙&lt;/a&gt; 中也有说明）；不过副作用就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 里面的方法在后台线程执行了（谁知道这是好处呢还是坏处呢 ^_^）。&lt;/p&gt;

&lt;p&gt;问题就在于，有些“耗时”操作根本就无法放入后台线程，典型的莫过于“耗时”的 UI 操作。本文将通过实现一个适用于 UI 的可等待类型来解决这种 UI 的“耗时”等待问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;awaiter-系列文章&quot;&gt;Awaiter 系列文章&lt;/h2&gt;

&lt;p&gt;入门篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实战篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;本文阅读建议&quot;&gt;本文阅读建议&lt;/h2&gt;

&lt;p&gt;本文&lt;strong&gt;代码较多&lt;/strong&gt;，阅读建议：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;标注为“&lt;strong&gt;本文推荐的完整代码&lt;/strong&gt;”的代码块可直接放入自己的项目中使用，也贴出了 GitHub 上我以 MIT 开源的源代码（可能 GitHub 上会经常更新）。&lt;/li&gt;
  &lt;li&gt;标注“&lt;strong&gt;此处为试验代码&lt;/strong&gt;”的代码块表明此处代码并不完善，仅用于本文分析使用，不建议放到自己的项目中使用。&lt;/li&gt;
  &lt;li&gt;没有注释标注的代码块是用于研究的代码片段，不需要使用。&lt;/li&gt;
  &lt;li&gt;可点击下面的导航跳转到你希望的地方。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;我们的需求&quot;&gt;我们的需求&lt;/h2&gt;

&lt;p&gt;这里说的 UI “耗时”，“耗时”打了引号，是因为严格来说并不是真的卡死了 UI，而是某个函数的执行需要更多的 UI 操作才能继续。这句话可能比较难懂，但举两个例子就好懂了。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;某个函数的执行需要显示一个用户控件，用户填写控件中的信息并确定后，函数才继续执行。这种感觉很像模态窗口，但我们却是在同一个窗口内实现，不能通过模态窗口来实现我们的功能。（UWP 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContentDialog&lt;/code&gt; 就是这么干的。）&lt;/li&gt;
  &lt;li&gt;我们需要在后台线程创建一个控件，创建完毕之后在原线程返回。这样我们就能得到一个在后台线程创建的控件了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本文将以实现第 2 条为目标，一步步完善我们的代码，并做出一个非常通用的 UI 可等待类出来。最终你会发现，我们的代码也能轻松应对第 1 条的需求。&lt;/p&gt;

&lt;h2 id=&quot;实现目标-dispatcherasyncoperation&quot;&gt;实现目标 DispatcherAsyncOperation&lt;T&gt;&lt;/T&gt;&lt;/h2&gt;

&lt;p&gt;现在，我们来实现我们的目标。&lt;/p&gt;

&lt;p&gt;回顾一下，我们希望实现一个方法，要求能够在后台线程创建一个 UI 控件。&lt;/p&gt;

&lt;p&gt;不使用自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt;，使用现有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 可以写出如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注：此处为试验代码。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UIDispatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AutoResetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;说明一下：&lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));&lt;/code&gt; 这句话是为了确保创建的新 UI 线程里执行的 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 代码在 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步等待之后能够继续回到此 UI 线程，而不是随便从线程池找一个线程执行。&lt;/p&gt;

&lt;p&gt;试一下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;确实拿到了后台线程创建的 UI 对象。&lt;/p&gt;

&lt;p&gt;然而，注意这一句：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里开启了一个新的线程，专门等待后台线程执行到某个关键位置，实在是太浪费。如果我们实现的是本文开头的第一个需求，需要等待用户输入完信息点击确认后才继续，那么这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WaitOne&lt;/code&gt; 则可能会等非常久的时间（取决于用户的心情，啥时候想点确定啥时候才结束）。&lt;/p&gt;

&lt;p&gt;线程池里一个线程就这样白白浪费了，可惜！可惜！&lt;/p&gt;

&lt;p&gt;于是，我们换自己实现的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt;，节省这个线程的资源。取个名字，既然用于 UI 线程使用，那么就命名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation&lt;/code&gt; 好了。我打算让这个类同时实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;IAwaitable&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;IAwaiter&lt;/code&gt; 接口，因为我又不会去反复等待，只用一次。&lt;/p&gt;

&lt;p&gt;那么开始，既然要去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt;，那么我们需要在后台线程真正完成任务的时候自动去执行接下来的任务，而不是在调用线程中去等待。&lt;/p&gt;

&lt;p&gt;经过反复修改，我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation&lt;/code&gt; 类如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 此段代码为本文推荐的完整版本。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 可复制或前往我的 GitHub 页面下载：&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.Sharing/Utils/Threading/DispatcherAsyncOperation.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Utils.Threading&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IAwaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigurePriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;reportResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解释一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Create()&lt;/code&gt; 静态方法会返回一个可以等待的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation&amp;lt;T&amp;gt;&lt;/code&gt; 实例，在写实现代码的地方当然不是用来等的，这个值是用来给外部使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的开发者返回的。但是，它会 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt;，调用这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt;，则可以报告操作已经结束。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 方法会在主线程调用的代码结束后立即执行。参数中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;continuation&lt;/code&gt; 是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 后面代码的一层包装，调用它即可让 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 后面的代码开始执行。但是，我们却并不是立即就能得到后台线程的返回值。于是我们需要等到后台线程执行完毕，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReportResult&lt;/code&gt; 方法的时候才执行。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_continuation += continuation;&lt;/code&gt; 需要使用 “+=” 是因为这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter()&lt;/code&gt; 返回的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;，也就是说，极有可能发生同一个实例被 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 多次的情况，需要将每次后面的任务都执行才行。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_continuation&lt;/code&gt; 可能为空，是因为任务执行完毕的时候也没有任何地方 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 了此实例。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在有了新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation&lt;/code&gt; 的帮助下，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher&lt;/code&gt; 改进成了如下模样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注：此处为试验代码。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UIDispatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaitable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了让 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher&lt;/code&gt; 更加通用，我们把后台线程创建 UI 控件的代码移除，现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher&lt;/code&gt; 里面只剩下用于创建一个后台线程运行的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的方法了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 此段代码为本文推荐的完整版本。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 可复制或前往我的 GitHub 页面下载：&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/UIDispatcher.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UIDispatcher&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunNewAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaitable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;BackgroundUI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;IsBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApartmentState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaitable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;回顾完整的代码&quot;&gt;回顾完整的代码&lt;/h2&gt;

&lt;p&gt;至此，我们得到了三个完整的代码文件（在 &lt;strong&gt;GitHub&lt;/strong&gt; 上，以下所有代码文件均有&lt;strong&gt;详尽的中文注释&lt;/strong&gt;）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Threading/AwaiterInterfaces.cs&quot;&gt;AwaiterInterfaces.cs&lt;/a&gt; 用于定义一组完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Awaitable&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Awaiter&lt;/code&gt; 接口，方便开发者实现自定义可等待对象。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.Sharing/Utils/Threading/DispatcherAsyncOperation.cs&quot;&gt;DispatcherAsyncOperation.cs&lt;/a&gt; 一个自定义的，适用于 UI 的自定义可等待（&lt;code class=&quot;highlighter-rouge&quot;&gt;awaitable&lt;/code&gt;）类；使用此类可以避免浪费一个线程用于等待 UI 操作的结束。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/UIDispatcher.cs&quot;&gt;UIDispatcher.cs&lt;/a&gt; 用于在后台线程启动一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;，以便在这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 中方便地创建控件。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;回顾需求&quot;&gt;回顾需求&lt;/h2&gt;

&lt;p&gt;现在，在以上三个完整代码文件的帮助下，我们实现我们的那两个需求。（手动斜眼一下，我只说拿第 2 个需求当例子进行分析，并不是说只实现第 2 个。我们的目标是写出一份通用的组件来，方便实现大部分主流需求。）&lt;/p&gt;

&lt;h3 id=&quot;实现第-2-个需求&quot;&gt;实现第 2 个需求&lt;/h3&gt;

&lt;p&gt;后台创建一个 UI 控件：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunNewAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以这样用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TextBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Opacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Margin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以这样用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还可以不用新建线程和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;，直接利用现成的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateElementAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;实现第-1-个需求&quot;&gt;实现第 1 个需求&lt;/h3&gt;

&lt;p&gt;显示一个用户控件，等用户点击了确定后异步返回：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShowAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaiter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherAsyncOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;awaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OkButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;_reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CancelButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;_reportResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以这样用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 用户点了确定。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 用户点了取消。。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;全文总结&quot;&gt;全文总结&lt;/h2&gt;

&lt;p&gt;读者读到此处，应该已经学会了如何自己实现一个自定义的异步等待类，也能明白某些场景下自己写一个这样的类代替原生 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的好处。不过不管是否明白，通过阅读本文还收获了三份代码文件呢！我已经把这些文件以 MIT 开源到了 &lt;a href=&quot;https://github.com/walterlv/sharing-demo&quot;&gt;walterlv/sharing-demo&lt;/a&gt; 中，大家可以随意使用。&lt;/p&gt;

&lt;p&gt;本文较长，如果阅读的过程中发现了任何不正确的地方，希望能回复帮我指出；如果有难以理解的地方，也请回复我，以便我能够调整我的语句，使之更易于理解。&lt;/p&gt;

&lt;p&gt;以上。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation&quot;&gt;Dixin’s Blog - Understanding C# async / await (1) Compilation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern&quot;&gt;Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/&quot;&gt;await anything; - Parallel Programming with .NET&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://liujiajia.me/blog/details/csharp-multi-threading-05-csharp6-08-customize-awaitable&quot;&gt;【C#】【多线程】【05-使用C#6.0】08-自定义awaitable类型 - L.M&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs&quot;&gt;AsyncMethodBuilder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 22 Dec 2018 11:54:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-dispatcher-awaiter-for-ui.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-dispatcher-awaiter-for-ui.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 小伙伴希望保存一个文件，并且希望如果出错了也要不断地重试。然而我认为如果一直错误则应该对外抛出异常让调用者知道为什么会一直错误。&lt;/p&gt;

&lt;p&gt;这似乎是一个矛盾的要求。然而最终我想到了一个办法：让重试一直进行下去，谁需要关心异常谁就去 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 异常，不需要关心异常的模块则跟着一直重试直到成功。&lt;/p&gt;

&lt;p&gt;我们通过编写一个自己的 Awaiter 来实现，本文将说明其思路和最终实现的代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;awaiter-系列文章&quot;&gt;Awaiter 系列文章&lt;/h2&gt;

&lt;p&gt;入门篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实战篇：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;遇到了什么问题&quot;&gt;遇到了什么问题&lt;/h2&gt;

&lt;p&gt;有一个任务，可能会出错，然而重试有可能可以解决。典型的例子是写入文件，你可能因为其他进程占用的问题而导致无法写入，然而一段时间之后重试是可以解决的。&lt;/p&gt;

&lt;p&gt;现在，不同业务对这同一个操作有不同的需求：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;有的业务不关心写入结果到底如何&lt;/li&gt;
  &lt;li&gt;有的业务由于时间有限，只能接受几次的重试&lt;/li&gt;
  &lt;li&gt;有的业务关心写入过程中的异常&lt;/li&gt;
  &lt;li&gt;而有的业务非常闲，只要一直写入就行了，最终成功告诉我就好&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-16-03-05.png&quot; alt=&quot;不同业务有不同的重试需求&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可是，我们如何在一个任务中同时对所有不同的业务需求进行不同种类的响应呢？&lt;/p&gt;

&lt;h2 id=&quot;思路&quot;&gt;思路&lt;/h2&gt;

&lt;p&gt;我的思路是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;当有业务发起请求之后，就开启一个不断重试的任务；&lt;/li&gt;
  &lt;li&gt;针对这个请求的业务，返回一个专为此业务定制的可等待对象；&lt;/li&gt;
  &lt;li&gt;如果在重试完成之前，还有新的业务请求发起，那么则返回一个专为此新业务定制的可等待对象；&lt;/li&gt;
  &lt;li&gt;一旦重试任务成功完成，那么所有的可等待对象强制返回成功；&lt;/li&gt;
  &lt;li&gt;而如果重试中有的可等待对象已经等待结束但任务依旧没有成功，则在可等待对象中引发任务重试过程中发生过的异常。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这样，任务不断重试。而且，无论多少个业务请求到来，都只是加入到循环中的一部分来，不会开启新的循环任务。每个业务的等待时长和异常处理都是自己的可等待对象中处理的，不影响循环任务的继续执行。&lt;/p&gt;

&lt;h2 id=&quot;关于源代码说明&quot;&gt;关于源代码说明&lt;/h2&gt;

&lt;p&gt;本文所述的所有源代码可以在 &lt;a href=&quot;https://gist.github.com/walterlv/d2aecd02dfad74279713112d44bcd358&quot;&gt;https://gist.github.com/walterlv/d2aecd02dfad74279713112d44bcd358&lt;/a&gt; 查看和下载到最新版本。&lt;/p&gt;

&lt;h3 id=&quot;期望如何使用这个新的-awaiter&quot;&gt;期望如何使用这个新的 Awaiter&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 记录一个可以重试的循环。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PartialAwaitableRetry&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 初始化一个可以重试的循环，循环内部执行的方法是 TryCoreAsync。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_loop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PartialAwaitableRetry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TryCoreAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果外界期望使用这个类试一下，那么就调用此方法。默认尝试 10 次，但也可以指定为 -1 尝试无数次。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContinuousPartOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tryCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 加入循环中，然后返回一个可以异步等待 10 次循环的对象。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;JoinAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 此方法就是循环的内部执行的方法。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OperationResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryCoreAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PartialRetryContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 每 1 秒执行一次循环重试，当然你也可以尝试指数退避。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 执行真正需要重试而且可能出现异常的方法。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingIOAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 如果执行成功，那么就返回 true。当然，上面的代码如果出现了异常，也是可以被捕获到的。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 这就是那个有可能会出错，然后出错了需要不断重试的方法。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingIOAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 省略实际执行的代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;写一个可以不断循环的循环并允许不同业务加入等待&quot;&gt;写一个可以不断循环的循环，并允许不同业务加入等待&lt;/h3&gt;

&lt;p&gt;上面的代码中，我们使用到了两个新的类型：用于循环执行某个委托的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PartialAwaitableRetry&lt;/code&gt;，以及用于表示单次执行结果的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OperationResult&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;以下只贴出此代码的关键部分，全部源码请至本文末尾查看或下载。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PartialAwaitableRetry&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略构造函数和部分字段，请至本文文末查看完整代码。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CountLimitOperationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tokens&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CountLimitOperationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContinuousPartOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JoinAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountLimitOperationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;countLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 省略线程安全代码，请至本文文末查看完整代码。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_isLooping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_loopItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 省略线程安全处理和异常处理，请至本文文末查看完整代码。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;维护一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CountLimitOperationToken&lt;/code&gt; 的集合，然后在每次循环的时候更新集合中的所有项。这样，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;JsonAsync&lt;/code&gt; 创建的每一个可等待对象就能更新其状态 —— 将异常传入或者将执行的次数传入。&lt;/p&gt;

&lt;p&gt;由于我们在创建可等待对象 &lt;code class=&quot;highlighter-rouge&quot;&gt;CountLimitOperationToken&lt;/code&gt; 的时候，传入了等待循环的次数，所以我么可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;CountLimitOperationToken&lt;/code&gt; 内部实现每次更新循环执行次数和异常的时候，更新其等待状态。如果次数已到，那么就通知异步等待完成。&lt;/p&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;OperationResult&lt;/code&gt; 类，是个简单的运算符重载，用于表示单次循环中的成功与否的状态和异常情况。可以在本文文末查看其代码。&lt;/p&gt;

&lt;h3 id=&quot;写一个可等待对象针对不同业务返回不同的可等待对象实例&quot;&gt;写一个可等待对象，针对不同业务返回不同的可等待对象实例&lt;/h3&gt;

&lt;p&gt;我写了三个不同的类来完成这个可等待对象：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CountLimitOperationToken&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;上面的代码中我们使用到了这个类型，目的是为了生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContinuousPartOperation&lt;/code&gt; 这个可等待对象。&lt;/li&gt;
      &lt;li&gt;我将这个 Token 和实际的 Awaitable 分开，是为了隔离执行循环任务的代码和等待循环任务的代码，避免等待循环任务的代码可以修改等待的过程。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ContinuousPartOperation&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这个是实际的可等待对象，这个类型的实例可以直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字进行异步等待，也可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait()&lt;/code&gt; 方法进行同步等待。&lt;/li&gt;
      &lt;li&gt;我把这个 Awaitable 和 Awaiter 分开，是为了隔离 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字的 API 和编译器自动调用的方法。避免编译器的大量方法干扰使用者对这个类的使用。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ContinuousPartOperation.Awaiter&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这是实际上编译器自动调用方法的一个类，有点类似于我们为了支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 而实现的 &lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerator&lt;/code&gt;。（而集合应该继承 &lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerable&lt;/code&gt;）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以其实这三个类是在干同一件事情，都是为了实现一个可 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步等待的对象。&lt;/p&gt;

&lt;p&gt;关于如何编写一个自己的 Awaiter，可以参考我的 Awaiter 入门篇章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/what-is-an-awaiter&quot;&gt;.NET 中什么样的类是可使用 await 异步等待的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/abstract-awaitable-and-awaiter&quot;&gt;定义一组抽象的 Awaiter 的实现接口，你下次写自己的 await 可等待对象时将更加方便&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-and-write-custom-awaiter&quot;&gt;.NET 除了用 Task 之外，如何自己写一个可以 await 的对象？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以及实战篇章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-dispatcher-awaiter-for-ui&quot;&gt;在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/write-an-awaiter-that-await-part-of-a-loop&quot;&gt;.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这几个类的实际代码可以在文末查看和下载。&lt;/p&gt;

&lt;h2 id=&quot;附全部源码&quot;&gt;附全部源码&lt;/h2&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/d2aecd02dfad74279713112d44bcd358.js&quot;&gt;&lt;/script&gt;

</description>
        <pubDate>Sat, 22 Dec 2018 11:50:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-an-awaiter-that-await-part-of-a-loop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-an-awaiter-that-await-part-of-a-loop.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件</title>
        <description>&lt;p&gt;你可以使用临界区（Critical Section）、互斥量（Mutex）、信号量（Semaphores）和事件（Event）来处理线程同步。然而，在编写一些异步处理函数，尤其是还有 async 和 await 使用的时候，还有一些更方便的类型可以用来处理线程同步。&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskCompletionSource&lt;/code&gt;，你可以轻松地编写既可以异步等待，又可以同步等待的代码来。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;等待事件&quot;&gt;等待事件&lt;/h2&gt;

&lt;p&gt;我们创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskCompletionSource&amp;lt;object&amp;gt;&lt;/code&gt; 对象，这样，我们便可以写出一个既可以同步等待又可以异步等待的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvDemo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskCompletionSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskCompletionSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WaitAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等待时可以同步：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以异步：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而同步的那个方法，便可以用来做线程同步使用。&lt;/p&gt;

&lt;h2 id=&quot;引发事件&quot;&gt;引发事件&lt;/h2&gt;

&lt;p&gt;要像一个事件一样让同步等待阻塞着的线程继续跑起来，则需要设置这个事件。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskCompletionSource&amp;lt;object&amp;gt;&lt;/code&gt; 提供了很多让任务完成的方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-15-47-52.png&quot; alt=&quot;TaskCompletionSource 中的方法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以通过让这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;TaskCompletionSource&amp;lt;object&amp;gt;&lt;/code&gt; 完成、取消或设置异常的方式让这个 Task 进入完成、取消或错误状态，然后等待它的线程就会继续执行；当然如果有异常，就会让等待的线程收到一个需要处理的异常。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 22 Dec 2018 07:50:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-task-completion-source-as-await-locker.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-task-completion-source-as-await-locker.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 WPF 开发一个 Windows 屏幕保护程序</title>
        <description>&lt;p&gt;最近有小伙伴问我如何可以让 Windows 静置一段时间不操作之后，显示一个特殊的界面。我想了想，屏幕保护程序可以做到这一点，而且，屏幕保护程序的开发也是非常简单的。&lt;/p&gt;

&lt;p&gt;本文将介绍如何为 Windows 这一悠久的功能进行开发。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;屏幕保护程序的本质&quot;&gt;屏幕保护程序的本质&lt;/h2&gt;

&lt;p&gt;屏幕保护程序本质上就是一个 Win32 窗口应用程序。&lt;/p&gt;

&lt;p&gt;好了，这一节真的结束了……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-09-58-32.png&quot; alt=&quot;屏幕保护程序的本质&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编译好一个窗口应用程序之后，把扩展名改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;scr&lt;/code&gt;，于是你的屏幕保护程序就做好了。&lt;/p&gt;

&lt;h2 id=&quot;安装屏幕保护程序&quot;&gt;安装屏幕保护程序&lt;/h2&gt;

&lt;p&gt;现在，在你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;scr&lt;/code&gt; 程序上点击右键，可以看到一个 “安装” 选项，点击之后就安装了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-10-01-29.png&quot; alt=&quot;安装屏幕保护程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;安装之后，你会立即看到我们的屏幕保护程序已经运行起来了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-10-06-36.png&quot; alt=&quot;首次运行的屏幕保护程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了方便截图，我调了下窗口大小。实际上本应该是 Visual Studio 创建的空 WPF 程序的默认大小。&lt;/p&gt;

&lt;h2 id=&quot;处理屏幕保护程序参数&quot;&gt;处理屏幕保护程序参数&lt;/h2&gt;

&lt;p&gt;我的屏幕保护程序是一个非常简单的程序，几乎就是默认的模板。只是，现在加上了一点文字，输出命令行参数。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.DirextXDemo.Wpf.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ScreenSaver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ArgsTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCommandLineArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在前面的截图中，我们看到参数是 “&lt;code class=&quot;highlighter-rouge&quot;&gt;/p 8457636&lt;/code&gt;”，这是表示此程序需要在预览窗格中进行预览。&lt;/p&gt;

&lt;p&gt;还有其他参数，用于处理其他情况：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/s&lt;/code&gt; 屏幕保护程序开始，或者用户点击了 “预览” 按钮&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/c:463970&lt;/code&gt; 用户点击了 “设置” 按钮&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/p 8457636&lt;/code&gt; 用户选中屏幕保护程序之后，在预览窗格中显示&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-10-15-32.png&quot; alt=&quot;屏幕保护程序参数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;实际上屏幕保护程序开始和预览是不同的。预览的时候，只会启动你的程序；而实际开始的时候，Windows 会先为你创建一个白色的背景，覆盖所有的屏幕，然后你的屏幕保护程序窗口显示在那个白色的背景之上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-22-10-24-22.png&quot; alt=&quot;实际上运行时，后面有白色的背景&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;请预防一些坑&quot;&gt;请预防一些坑&lt;/h2&gt;

&lt;p&gt;你可能会发现 Windows 自带的屏幕保护程序在 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\System32&lt;/code&gt; 文件夹中。但！那不是你放屏幕保护程序的地方！如果把你的屏幕保护程序拷贝到那个 Windows 的受信任目录下，你的程序是无法运行起来的。正确的做法，是右键，使用 “安装” 选项进行安装。&lt;/p&gt;

&lt;p&gt;我后面附的链接中可能说屏幕保护程序还要有一些其他的要求，例如必须全屏、不要显示到任务栏等等。但那其实并不是强制性的要求，比如本文就显示了一个普通的窗口。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wbsimms.com/create-screensaver-net-wpf/&quot;&gt;Create a screensaver with .NET and WPF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 22 Dec 2018 02:24:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-a-windows-screen-saver-using-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-a-windows-screen-saver-using-wpf.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>优化 UWP 中图片的内存占用</title>
        <description>&lt;p&gt;跟图片打交道的 UWP 应用或多或少都会遇到图片带来的性能问题，就算不主要处理图片，做个论坛做个新闻客户端都涉及到大量图片。一个帖子、一篇文章里多半都是些高清大图，这些图片一张即可占用程序 1~2M 的内存空间。普通的写法内存很快就爆了，那么 UWP 中我们可以用哪些方法优化呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-decodepixelwidthdecodepixelheight&quot;&gt;1. DecodePixelWidth/DecodePixelHeight&lt;/h2&gt;

&lt;p&gt;对于那些高分辨率图像，直接设置其 &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelWidth&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelHeight&lt;/code&gt; 的值为较小的值即可大大节省内存空间。以下两种方法中，后者对内存空间的节省非常显著。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 性能不好 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ms-appx:///static/posts/high-resolution-image.jpg&quot;&lt;/span&gt; 
       &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;300&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 性能不错 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image.Source&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;BitmapImage&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;UriSource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ms-appx:///static/posts/high-resolution-image.jpg&quot;&lt;/span&gt; 
                     &lt;span class=&quot;na&quot;&gt;DecodePixelWidth=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;300&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DecodePixelHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image.Source&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-利用好自带的布局机制&quot;&gt;2. 利用好自带的布局机制&lt;/h2&gt;

&lt;p&gt;如果没有指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelWidth&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;DecodePixelHeight&lt;/code&gt;，那么 XAML 会根据布局自动调整图片的解码大小。&lt;/p&gt;

&lt;p&gt;不过，微软才不会让你这么开心地就用！如果你做了这些事，就当布局自带的内存优化不存在好了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;你先调了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetSourceAsync&lt;/code&gt; 或设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;UriSource&lt;/code&gt;，然后才把 &lt;code class=&quot;highlighter-rouge&quot;&gt;BitmapImage&lt;/code&gt; 连接到活动的 XAML 树&lt;/li&gt;
  &lt;li&gt;使用异步解码（如 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetSource&lt;/code&gt;）解码图像。&lt;/li&gt;
  &lt;li&gt;把图像或父控件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Opacity&lt;/code&gt; 设成了 0，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Collapsed&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ImageBrush&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strech&lt;/code&gt; 设成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;图像用作点九图（参见 &lt;a href=&quot;https://docs.microsoft.com/zh-cn/uwp/api/Windows.UI.Xaml.Controls.Image#Windows_UI_Xaml_Controls_Image_NineGrid?wt.mc_id=MVP&quot;&gt;NineGrid&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;给图像或父控件设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;CacheMode=&quot;BitmapCache&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ImageBrush&lt;/code&gt; 绘制到不是矩形的地方 (试过画到文字上或形状上吗？)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于第 1 条，这里有一些官方的代码作为例子：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 推荐写法，直接在 XAML 里指定 UriSoruce --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myImage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;UriSource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets/cool-image.png&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 但如果没有在 XAML 中指定，也可以去后台代码指定。 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myImage&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 后台代码如果这样写就不错，因为先把 BitmapImage 放到了活动的 XAML 树上。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;myImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UriSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ms-appx:///static/posts/cool-image.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RelativeOrAbsolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 然而这样写就不太推荐了，因为先设置了 UriSource，再把 BitmapImage 放到活动的 XAML 树上。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UriSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ms-appx:///static/posts/cool-image.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UriKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RelativeOrAbsolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;myImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-利用好自带的缓存机制&quot;&gt;3. 利用好自带的缓存机制&lt;/h2&gt;

&lt;p&gt;如果你用 &lt;code class=&quot;highlighter-rouge&quot;&gt;UriSource&lt;/code&gt; 属性，那么恭喜，你将获得自带的图片缓存！如果多次使用相同的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Uri&lt;/code&gt;，那么会共用同一份内存空间。除此之外就没啦，比如自己创建一个流啊什么的；这就是说并不建议自己用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileStream&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;另外，微软提供了这么好用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetSourceAsync&lt;/code&gt;，但是用了这个就没有缓存了！于是我到底是用还是不用呢？&lt;/p&gt;

&lt;h2 id=&quot;4-getthumbnailasync&quot;&gt;4. GetThumbnailAsync&lt;/h2&gt;

&lt;p&gt;如果你使用本机文件，那么恭喜，你直接获得了拿到系统自带缩略图的机会！&lt;/p&gt;

&lt;p&gt;使用系统自带的缩略图比前面的方法都更好，因为如果系统已经生成好了缩略图，你根本连解码图像都不需要。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;FileOpenPicker&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileOpenPicker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileTypeFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.bmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileTypeFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileTypeFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.jpeg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileTypeFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SuggestedStartLocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PickerLocationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PicturesLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;StorageFile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;picker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PickSingleFileAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;StorageItemThumbnail&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileThumbnail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetThumbnailAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThumbnailMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SingleItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;BitmapImage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BitmapImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileThumbnail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetThumbnailAsync&lt;/code&gt; 的详细用法，我的好朋友林德熙有更详细的说明，参见：&lt;a href=&quot;https://blog.lindexi.com//post/win10-uwp-%E8%8E%B7%E5%BE%97%E7%BC%A9%E7%95%A5%E5%9B%BE.html&quot;&gt;win10 uwp 获得缩略图&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 21 Dec 2018 08:51:33 +0000</pubDate>
        <link>https://blog.walterlv.com/uwp/2017/09/17/optimize-image-in-uwp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/uwp/2017/09/17/optimize-image-in-uwp.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>文件和文件夹不存在的时候，FileSystemWatcher 监听不到文件的改变？如果递归地监听就可以了</title>
        <description>&lt;p&gt;当你需要监视文件或文件夹的改变的时候，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 便可以完成。不过，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 对文件夹的监视要求文件夹必须存在，否则会产生错误“无效路径”。&lt;/p&gt;

&lt;p&gt;那么，如果文件或文件夹不存在的时候可以怎么监视文件的改变呢？更麻烦的是如果顶层很多级文件夹都不存在，怎么能监视呢？本文将告诉你方法。&lt;/p&gt;

&lt;p&gt;本文的代码适用于 .NET Framework 和 .NET Core，同时不需要任何第三方依赖。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;方法一创建文件夹在逃避问题但也不失为一种解决思路&quot;&gt;方法一：创建文件夹（在逃避问题，但也不失为一种解决思路）&lt;/h2&gt;

&lt;p&gt;如果文件夹不存在，把它创建出来就可以监视了嘛！这其实是在逃避问题。不过我把它写出来是因为如果我不说，可能有些小伙伴原本简单的问题就会变得复杂化。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFullPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果文件夹不存在，则创建文件夹。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 监视 directory 文件夹下的 file 文件的改变&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NotifyFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotifyFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FinalFile_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 使用 watcher 做其他的事情。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FinalFile_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 当文件改变的时候，这里的代码会执行。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的含义是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将文件路径取出来，分为文件夹部分和文件部分；&lt;/li&gt;
  &lt;li&gt;判断文件夹是否存在，如果不存在，则创建文件夹；&lt;/li&gt;
  &lt;li&gt;监视文件夹中此文件的改变。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;需要说明的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 原本是监视文件夹的，第一个参数是监视的文件夹的路径，而第二个参数是监视文件或文件夹的过滤通配符。&lt;/p&gt;

&lt;p&gt;不过，官方文档的说明是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;To watch a specific file, set the Filter property to the file name. For example, to watch for changes in the file MyDoc.txt, set the Filter property to “MyDoc.txt”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果你需要监听一个特定的文件，那么直接将后面的过滤器设定为文件名，那么就会直接监视到对应的文件。&lt;/p&gt;

&lt;p&gt;如果你的业务当中，反正始终都是要创建这个文件的，那么一开始创建了这个文件夹就能避免不少的麻烦。这也是我把这个方法放到这里作为首选方法的原因。虽然实际上这是在逃避问题，但真的是一个好方法。&lt;/p&gt;

&lt;h2 id=&quot;方法二递归监视文件夹&quot;&gt;方法二：递归监视文件夹&lt;/h2&gt;

&lt;p&gt;这种方法适用于如果文件或者文件夹不存在时，你不能创建这个文件夹的情况。也许是你的业务需要，也许因为你正在写库，库作为最为通用的业务，不希望改变用户的环境。&lt;/p&gt;

&lt;p&gt;这时，我们可以考虑的思路是 —— &lt;strong&gt;递归地监视文件或文件夹&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;例如，我们有这样的文件夹结构：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;C:\a\b\x.txt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;希望监听 x.txt 的改变。&lt;/p&gt;

&lt;p&gt;那么，如果 b 文件夹不存在，就监听 a 文件夹，如果 a 文件夹也不存在，那么就监听 C: 驱动器。实际上，我们不需要再去考虑 C: 驱动器也不存在的情况了（当你真的遇到的时候，考虑业务上规避吧……）。&lt;/p&gt;

&lt;h3 id=&quot;代码实现&quot;&gt;代码实现&lt;/h3&gt;

&lt;p&gt;既然需要递归监视，那么我们需要查找第一次监视的时候，需要到哪一层。&lt;/p&gt;

&lt;p&gt;这里，我们可以用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环来进行，一层一层查找文件夹。直到能够找到一层，文件夹存在而子文件夹不存在的情况。这时我们便能够监视子文件夹的创建了。&lt;/p&gt;

&lt;p&gt;我写了一个函数，用于返回这时存在的那个文件夹，和不存在的那个子文件夹或者文件。&lt;/p&gt;

&lt;p&gt;当然有特殊情况，就是文件直接就已经存在的情况下，也是返回文件所在的文件夹和此文件名的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWatchableLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果文件存在，就返回文件所在的文件夹和文件本身。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果文件不存在，但文件夹存在，也是返回文件夹和文件本身。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这一点在下面的第一层循环中体现。&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 对于每一层循环。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 检查文件夹是否存在，只要文件夹存在，那么就可以返回。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 如果连文件夹都不存在，那么就需要查找上一层文件夹。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来，根据得到的文件夹和文件，判断其存在与否，决定是监视这个文件的改变，还是监视文件/文件夹结构的改变。如果文件/文件夹的结构改变，那么就需要重新调用这个方法再查找应该监视的文件夹了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWatchableLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果文件存在，说明这是最终的文件。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 注意使用 File.Exists 判断已存在的同名文件夹时会返回 false。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;NotifyFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotifyFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FinalFile_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileOrDirectory_CreatedOrDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 注意这里的 file 可能是文件也可能是文件夹。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileSystemWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileOrDirectory_CreatedOrDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Renamed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileOrDirectory_CreatedOrDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileOrDirectory_CreatedOrDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Exists(_file.FullName)&lt;/code&gt; 来判断最终的文件是否存在，用以区分是在监视最终的文件改变，还是监视文件夹结构的改变。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileOrDirectory_CreatedOrDeleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在文件/文件夹结构发生改变的时候，重新监视。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FinalFile_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里就是最终文件改变的地方了。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;完整的代码和使用方法&quot;&gt;完整的代码和使用方法&lt;/h3&gt;

&lt;p&gt;由于代码还是有一点点多。如果放到你原有的业务当中，对你的业务代码确实是一种污染。所以我封装了一个类 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileWatcher&lt;/code&gt;。它不需要依赖任何就可以使用，你可以将它拷贝到你的项目当中。&lt;/p&gt;

&lt;p&gt;代码可以阅读本文文末，或者前往 gist 查看：&lt;a href=&quot;https://gist.github.com/walterlv/cffec6dd951780ea946feb2ea96f302a&quot;&gt;FileWatcher that helps you to watch a single file change even if the file or it’s owner folders does not exists.&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;使用方法与 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 类似，但是更简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\walterlv\Desktop\demo.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnFileChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_watcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnFileChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 最纯粹的文件改变事件，仅在文件的内容真的改变的时候触发。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;此方法的特点优势和不足&quot;&gt;此方法的特点，优势和不足&lt;/h3&gt;

&lt;p&gt;实际上，&lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt; 的监视也是有一些空洞的。如果你只是监视一级文件夹而不是递归监视子文件夹（通过设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;IncludeSubdirectories&lt;/code&gt; 属性来指定），那么就会存在一些情况是监视不到的。然而如果你真的递归监视子文件夹，又会监听到大量的事件需要过滤。&lt;/p&gt;

&lt;p&gt;那么此方法可以支持和不支持的情况有哪些呢？&lt;/p&gt;

&lt;p&gt;依然假设监视的文件是：&lt;em&gt;C:\a\b\x.txt&lt;/em&gt; 。&lt;/p&gt;

&lt;p&gt;支持这些情况：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一开始文件 x.txt 不存在，而后创建。&lt;/li&gt;
  &lt;li&gt;一开始 b\x.txt 不存在，而后依次创建。&lt;/li&gt;
  &lt;li&gt;从 y.txt 文件重命名到 x.txt。&lt;/li&gt;
  &lt;li&gt;一开始文件 x.txt 存在，而后删除，再然后重新创建。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不支持这些情况：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一开始文件存在，但你直接删除了 a 或者 b 文件夹，而不是先删除了 x.txt。&lt;/li&gt;
  &lt;li&gt;一开始文件存在，但直接将 b\x.txt 连文件带文件夹一起移走，然后删除文件或文件夹。&lt;/li&gt;
  &lt;li&gt;一开始 b\x.txt 都不存在，但现在保持文件夹结构连文件带文件夹一起移入到 a 文件夹中。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，也有一些意外的发现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一开始文件存在，但直接将 b\x.txt 连文件带文件夹一起移走，这时依然能监听到 x.txt 文件的改变，但它已经不在原来的目录了。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;附所有源码&quot;&gt;附所有源码&lt;/h3&gt;

&lt;p&gt;如果看不到，请访问：&lt;a href=&quot;https://gist.github.com/walterlv/cffec6dd951780ea946feb2ea96f302a&quot;&gt;FileWatcher that helps you to watch a single file change even if the file or it’s owner folers does not exists.&lt;/a&gt;。&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/cffec6dd951780ea946feb2ea96f302a.js&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=netframework-4.7.2&quot;&gt;FileSystemWatcher Class (System.IO) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/29602014/6233938&quot;&gt;c# - How can i use FileSystemWatcher to watch directory if directory not exist? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/58740/FileSystemWatcher-Pure-Chaos-Part-1-of-2&quot;&gt;FileSystemWatcher - Pure Chaos (Part 1 of 2) - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/58741/FileSystemWatcher-Pure-Chaos-Part-2-of-2&quot;&gt;FileSystemWatcher - Pure Chaos (Part 2 of 2) - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/3251/Recursive-Directory-Watch&quot;&gt;Recursive Directory Watch - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.grasshopper3d.com/forum/topics/how-does-read-file-watch-for-changes&quot;&gt;How does Read File watch for changes? - Grasshopper&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17719905/reading-file-after-writing-it&quot;&gt;c# - Reading file after writing it - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 20 Dec 2018 02:05:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/watch-file-change-even-the-file-or-directory-not-exist.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/watch-file-change-even-the-file-or-directory-not-exist.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>StyleCop 是什么，可以帮助团队带来什么价值？</title>
        <description>&lt;p&gt;StyleCop 本质上是一个 C# 源代码规则分析器，可以帮助团队成员强制执行一组代码样式和一致性规则。&lt;/p&gt;

&lt;p&gt;本文将简述 StyleCop 以及它能为团队带来的价值。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;stylecop-是什么&quot;&gt;StyleCop 是什么？&lt;/h2&gt;

&lt;p&gt;StyleCop 本质上是一个 C# 源代码规则分析器，可以帮助团队成员强制执行一组代码样式和一致性规则。&lt;/p&gt;

&lt;p&gt;划重点 —— “&lt;strong&gt;强制&lt;/strong&gt;”。只要你愿意，你甚至可以让多写了一个空格的小伙伴无法成功编译项目！！！&lt;/p&gt;

&lt;h2 id=&quot;stylecop-能做什么不能做什么&quot;&gt;StyleCop 能做什么，不能做什么？&lt;/h2&gt;

&lt;p&gt;实际在团队中使用的时候，StyleCop 有三种不同的方式为我们所用：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;作为静态检查工具检查代码格式化规范；&lt;/li&gt;
  &lt;li&gt;作为编写代码时的自动格式化规则；&lt;/li&gt;
  &lt;li&gt;作为 API 扩展自定义的源代码检查的规则。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过，StyleCop 没有原生提供可以帮助辅助编写符合 StyleCop 规则的代码的工具或插件。也就是说，如果你希望编写出符合 StyleCop 规范的代码，那么你可能需要手工编写，调整格式。&lt;/p&gt;

&lt;p&gt;如果你的团队所有成员都是用 ReSharper，那么可以将 StyleCop 的规则也配置一遍到 ReSharper 中，这样编写时便可以符合 StyleCop 中定义的规范。&lt;/p&gt;

&lt;p&gt;关于使用 ReSharper 编写符合 StyleCop 规范的代码，可以参见：&lt;a href=&quot;/post/write-code-with-stylecop-using-resharper&quot;&gt;使用 ReSharper，输入即遵循 StyleCop 的代码格式化规范&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;stylecop-的优势和价值&quot;&gt;StyleCop 的优势和价值&lt;/h2&gt;

&lt;p&gt;StyleCop 的最大优势在于其“&lt;strong&gt;强制性&lt;/strong&gt;”。无论你使用哪种 IDE 进行开发，由于其检查过程可以嵌入到编译过程中，所以如果你开发出不符合 StyleCop 规范要求的代码，直接可以无法成功编译项目。对于格式或其他代码风格要求非常高的项目，可以持续保持项目的一致性。&lt;/p&gt;
</description>
        <pubDate>Sat, 15 Dec 2018 08:14:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/what-is-stylecop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/what-is-stylecop.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>UWP 中的 LaunchUriAsync，使用默认浏览器或其他应用打开链接</title>
        <description>&lt;p&gt;古老的 Win32 应用启动其他程序太过方便，以至于一部分开发者都已经不记得 Windows 能通过关联协议（参见 &lt;a href=&quot;/windows/2015/07/07/associate-with-file-or-protocol.html&quot;&gt;桌面应用程序关联协议&lt;/a&gt;）的方式通过统一资源定位符（URI）来启动应用程序了。&lt;/p&gt;

&lt;p&gt;转到 UWP 后，使用 URI 启动应用似乎成为了最推荐的方式。于是一句 &lt;code class=&quot;highlighter-rouge&quot;&gt;LaunchUriAsync&lt;/code&gt; 就能解决大多数问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;常用的-windows-10-内置协议&quot;&gt;常用的 Windows 10 内置协议&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;URI 协议&lt;/th&gt;
      &lt;th&gt;启动&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;http:&lt;/td&gt;
      &lt;td&gt;默认网页浏览器&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;mailto:&lt;/td&gt;
      &lt;td&gt;默认电子邮件&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ms-settings:&lt;/td&gt;
      &lt;td&gt;设置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ms-store:&lt;/td&gt;
      &lt;td&gt;应用商店&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;于是，只要 URI 带这些协议头，就能够用表格中的那些应用打开相应的功能了。&lt;/p&gt;

&lt;p&gt;如果想知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;ms-settings&lt;/code&gt; 里有哪些可用，请参见：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/launch-settings-app&quot;&gt;启动 Windows 设置应用 - UWP app developer&lt;/a&gt;；想知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;ms-store&lt;/code&gt; 可以如何帮助我们前往商店的具体页面，请参见：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/launch-store-app?wt.mc_id=MVP&quot;&gt;启动 Windows 应用商店应用 - UWP app developer&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;launchuriasync&quot;&gt;LaunchUriAsync&lt;/h2&gt;

&lt;p&gt;要想简单地在代码中使用，一句足以：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LaunchUriAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;https://blog.walterlv.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你希望在调用成功或失败后执行一些操作，则可以多写一些：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myblog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;https://blog.walterlv.com/blog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LaunchUriAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myblog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// 如果你感兴趣，可以在成功启动后在这里执行一些操作。&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// 如果你感兴趣，可以在这里处理启动失败的一些情况。&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，UWP 还提供了更多的选项：&lt;code class=&quot;highlighter-rouge&quot;&gt;LauncherOptions&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;launcheroptions&quot;&gt;LauncherOptions&lt;/h2&gt;

&lt;p&gt;在写以上代码时不难发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;LaunchUriAsync&lt;/code&gt; 提供了重载传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;LauncherOptions&lt;/code&gt; 参数，这个参数似乎是指定启动时的一些选项。查看注释后，可以发现这些选项：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置指示启动与文件或 URI 关联的应用程序时系统是否应显示文件或 URI 可能会不安全的警告的值。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果应显示警告，则为 true；否则为 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TreatAsUntrusted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置一个值，该值指示每当调用关联启动 API 时是否要显示**打开方式**对话框。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果应始终显示**打开方式**对话框，则为 true；否则为 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayApplicationPicker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;在启动默认应用程序时获取用户界面 (UI) 选项。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;UI 选项。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LauncherUIOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置表示没有处理文件类型或 URI 的应用程序时，用户应安装的应用程序在存储区中的包系列名称的值。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;应用的程序包系列名称。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PreferredApplicationPackageFamilyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置一个值，该值表示没有处理文件类型或 URI 的应用程序时，用户应安装的应用程序在存储区中的显示名称。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;应用程序的显示名称。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PreferredApplicationDisplayName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置表示没有处理文件类型或 URI 的应用程序时，用户应转到的浏览器中的 URI 的值。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;用户应转到的浏览器中的 URI。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FallbackUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置与表示网络上文件的 URI 相关的内容类型。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;URL 的内容类型。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;启动目标应用程序，并通过与目标应用程序平分空间或占用比目标应用程序更多或更少的空间，让当前运行的源应用程序保留在屏幕上。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;Windows.UI.ViewManagement.ViewSizePreference 类型的值，指定应用程序所需的视图大小。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ViewSizePreference&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DesiredRemainingView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;启动文件或 URI 时应使用的目标包的包系列名称。 此属性是可选的。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;启动文件或 URI 时应使用的目标包的包系列名称。 此属性是可选的。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetApplicationPackageFamilyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;让应用能访问与用于激活应用的文件相关的文件。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;包含相关文件列表的查询。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StorageFileQueryResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NeighboringFilesQuery&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;指示是否忽略可以处理 http(s) 方案（如浏览器）的处理程序。 相反，启动将回退到默认浏览器。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;**true** 指示可以处理 http(s) 方案的应用程序将被忽略，而是在默认浏览器中打开该 URI；否则为 **false**。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IgnoreAppUriHandlers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;获取或设置是否将启动器的选取器限制为当前应用程序及其相关联的 URI 处理程序。&amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果启动器应将选取器限制为当前应用程序及其相关联的 URI 处理程序，则为 true；否则为 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LimitPickerToCurrentAppAndAppUriHandlers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;TreatAsUntrusted&lt;/code&gt; 表示标记此次打开是不受信任的。&lt;/p&gt;

&lt;p&gt;如果打开程序自己内置的链接，通常置为 false，以便能直接打开。但有时程序需要处理用户输入的数据，这时就不一定真的是期望打开了。于是标记为不安全后，Windows 10 会为我们弹出一个提示框，告诉我们是否真的要切换应用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-25-00-18-22.png&quot; alt=&quot;Did you mean to switch apps&quot; /&gt;&lt;/p&gt;

&lt;p&gt;截图中的 MarkdownMail 是我的一个开源项目，可以前往 &lt;a href=&quot;https://github.com/walterlv/markdown-mail&quot;&gt;markdown-mail @ github&lt;/a&gt; 多多支持。&lt;/p&gt;

&lt;p&gt;我们还可以指定推荐用哪个应用打开（设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;PreferredApplicationPackageFamilyName&lt;/code&gt;），指定期望显示的窗口大小（设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesiredRemainingView&lt;/code&gt;，不过不是具体的大小，而是几种选项），指定只打开自己当前这款应用（设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;LimitPickerToCurrentAppAndAppUriHandlers&lt;/code&gt;）。具体查看注释是能够了解的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/launch-default-app?wt.mc_id=MVP&quot;&gt;启动 URI 的默认应用 - UWP app developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/launch-settings-app?wt.mc_id=MVP&quot;&gt;启动 Windows 设置应用 - UWP app developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/launch-store-app?wt.mc_id=MVP&quot;&gt;启动 Windows 应用商店应用 - UWP app developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:33 +0000</pubDate>
        <link>https://blog.walterlv.com/uwp/2017/09/25/launch-uri-async.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/uwp/2017/09/25/launch-uri-async.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>WPF/UWP 绑定中的 UpdateSourceTrigger</title>
        <description>&lt;p&gt;在开发 &lt;a href=&quot;https://github.com/walterlv/markdown-mail&quot;&gt;markdown-mail&lt;/a&gt; 时遇到了一些诡异的情况。代码是这么写的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Text, Mode=TwoWay}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而在 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextChanged&lt;/code&gt; 事件之后延时执行了一些操作时，从 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 里拿到的值却始终是旧的。&lt;/p&gt;

&lt;p&gt;阅读本文将了解其原因和解决办法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;无论是 WPF 还是 UWP，&lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 中都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpdateSourceTrigger&lt;/code&gt; 属性。&lt;/p&gt;

&lt;p&gt;在 WPF 中，其可取的值为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateSourceTrigger&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LostFocus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Explicit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 UWP 中，其可取的值为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateSourceTrigger&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Explicit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这些值代表的含义是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Default&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;默认值，多数情况下与 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyChanged&lt;/code&gt; 一样，然而&lt;strong&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextBox.Text&lt;/code&gt; 属性来说，却是 LostFocus（WPF）或 Explicit（UWP）&lt;/strong&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Explicit&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;必须在显式地调用 BindingExpression.UpdateSource 的情况下才会更新源值。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LostFocus&lt;/code&gt;（WPF 专属，不过 UWP 的预览版里也有）
    &lt;ul&gt;
      &lt;li&gt;目标控件失去焦点的时候更新源值。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyChanged&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;绑定的目标值改变的时候就会更新源值，至于检测方法，则完全由 WPF/UWP 的绑定系统完成&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，为了解决一开始的问题，我们需要在 TextBox 的 Text 属性的双向绑定里重新设置新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UpdateSourceTrigger&lt;/code&gt; 的值。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没错，就是加这半句就好了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-control-when-the-textbox-text-updates-the-source?wt.mc_id=MVP&quot;&gt;How to: Control When the TextBox Text Updates the Source - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/22253211/wpf-two-way-binding-not-working&quot;&gt;c# - WPF two-way binding not working - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.wpf-tutorial.com/data-binding/the-update-source-trigger-property/&quot;&gt;The UpdateSourceTrigger property - The complete WPF tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.updatesourcetrigger?wt.mc_id=MVP&quot;&gt;UpdateSourceTrigger Enum (Windows.UI.Xaml.Data) - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.data.updatesourcetrigger?wt.mc_id=MVP&quot;&gt;UpdateSourceTrigger Enum (System.Windows.Data) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.controls.textbox.text#System_Windows_Controls_TextBox_Text?wt.mc_id=MVP&quot;&gt;TextBox.Text Property (System.Windows.Controls) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:33 +0000</pubDate>
        <link>https://blog.walterlv.com/uwp/2017/09/25/binding-update-source-trigger.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/uwp/2017/09/25/binding-update-source-trigger.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>用微软拼音快速输入自定义格式的时间和日期</title>
        <description>&lt;p&gt;用微软拼音输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;rq&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;sj&lt;/code&gt;，我们可以在第五个候选词分别得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;2017年9月18日&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;22点05分&lt;/code&gt;。但是，其实我想输入的是：&lt;code class=&quot;highlighter-rouge&quot;&gt;2017-09-18&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;2017-09-18 22:05:51 +0800&lt;/code&gt;。在设置里面折腾一番没找到之后，原本以为只好自己输入了，没想到后面还是找到了方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我不会告诉你我要一个这样的日期是为了写这篇博客的时候能方便地填写编辑时间……&lt;/p&gt;

&lt;h2 id=&quot;效果&quot;&gt;效果&lt;/h2&gt;

&lt;p&gt;这是我们平时输入日期和时间的方法和效果：&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-03-01.png&quot; alt=&quot;日期&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-03-14.png&quot; alt=&quot;时间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是通过阅读本文可以达到的效果：&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-08-02.png&quot; alt=&quot;横线分割的日期&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-10-44.png&quot; alt=&quot;UTC 时间&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;方法&quot;&gt;方法&lt;/h2&gt;

&lt;p&gt;Windows 10：&lt;code class=&quot;highlighter-rouge&quot;&gt;设置&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;时间和语言&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;区域和语言&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;中文(中华人民共和国)&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;微软拼音&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;选项&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;词库和自学习&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;添加新的或编辑现有的用户自定义短语&lt;/code&gt;→&lt;code class=&quot;highlighter-rouge&quot;&gt;添加&lt;/code&gt;。&lt;br /&gt;
（不想吐槽为什么藏得这么深……）&lt;/p&gt;

&lt;p&gt;然后在短语里面输入以下代码：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;%yyyy%-%MM%-%dd% %HH%:%mm%:%ss% +0800
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-18-22-20-20.png&quot; alt=&quot;添加 UTC 时间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一些基本的格式化字符串是：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;变量&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;yyyy&lt;/td&gt;
      &lt;td&gt;4 位年&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MM&lt;/td&gt;
      &lt;td&gt;2 位月&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dd&lt;/td&gt;
      &lt;td&gt;2 位日&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;HH&lt;/td&gt;
      &lt;td&gt;2 位小时（24 小时制）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;mm&lt;/td&gt;
      &lt;td&gt;2 位分钟数&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ss&lt;/td&gt;
      &lt;td&gt;2 位秒数&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;更多格式化变量请参考：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings?wt.mc_id=MVP&quot;&gt;Custom Date and Time Format Strings&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;重要的说明&quot;&gt;重要的说明&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;需要注意，你只能在添加的时候这么写！&lt;/li&gt;
  &lt;li&gt;需要注意，你只能在添加的时候这么写！&lt;/li&gt;
  &lt;li&gt;需要注意，你只能在添加的时候这么写！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果是添加后再编辑，你会发现日期已经变成你点击“编辑”那一刻的时间固定下来了，你必须再把上面的代码敲一遍保存才行！&lt;/p&gt;

&lt;h3 id=&quot;如果你找不到设置到底在哪里下面是附图&quot;&gt;如果你找不到设置到底在哪里，下面是附图&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-18-22-19-20.png&quot; alt=&quot;设置&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-13-36.png&quot; alt=&quot;语言选项&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-14-05.png&quot; alt=&quot;输入法选项&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-16-36.png&quot; alt=&quot;编辑短语&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-18-22-17-08.png&quot; alt=&quot;添加短语&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:33 +0000</pubDate>
        <link>https://blog.walterlv.com/ime/2017/09/18/date-time-format-using-microsoft-pinyin.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/ime/2017/09/18/date-time-format-using-microsoft-pinyin.html</guid>
        
        
        <category>ime</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 中使用 EditorConfig 统一代码风格（含原生与插件）</title>
        <description>&lt;p&gt;EditorConfig 是一种被各种编辑器广泛支持的配置，使用此配置有助于项目在整个团队中保持一致的代码风格。Visual Studio 2017 开始原生支持 EditorConfig。&lt;/p&gt;

&lt;p&gt;本文将介绍 Visual Studio 对 EditorConfig 的支持情况（含原生与插件），并给出符合 .NET 和 C# 约定的 EditorConfig 详细设置。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;editorconfig-的广泛支持&quot;&gt;EditorConfig 的广泛支持&lt;/h2&gt;

&lt;p&gt;在 EditorConfig 官网中，贴出了一些可以纯原生无需任何插件支持 EditorConfig 代码风格配置的编辑器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-10-34-36.png&quot; alt=&quot;原生支持 EditorConfig 的编辑器&quot; /&gt;&lt;br /&gt;
▲ 原生支持 EditorConfig 的编辑器&lt;/p&gt;

&lt;p&gt;然后还贴出了可以通过插件支持的编辑器：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-10-35-36.png&quot; alt=&quot;可以通过插件支持 EditorConfig 的编辑器&quot; /&gt;&lt;br /&gt;
▲ 可以通过插件支持 EditorConfig 的编辑器&lt;/p&gt;

&lt;p&gt;EditorConfig 本身只定义了一个核心集，表示所有语言都共同遵循的代码格式规范：&lt;a href=&quot;https://editorconfig.org/#supported-properties&quot;&gt;EditorConfig 属性的核心集&lt;/a&gt;。同时，还有一些其他定义的规范：&lt;a href=&quot;https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties&quot;&gt;EditorConfig 的完整属性&lt;/a&gt;，不过这里不包括语言特定的规范。&lt;/p&gt;

&lt;h2 id=&quot;visual-studio-对-editorconfig-的支持程度&quot;&gt;Visual Studio 对 EditorConfig 的支持程度&lt;/h2&gt;

&lt;p&gt;Visual Studio 2017 开始添加了对 EditorConfig 的原生支持（你当然能在上面看到 Visual Studio 的图标啦）。&lt;/p&gt;

&lt;p&gt;原生的 Visual Studio 2017 支持 EditorConfig 属性的核心集和一些语言的特定属性。具体来说，是这一些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;核心属性
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;indent_style&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;indent_size&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tab_width&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;end_of_line&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;charset&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;trim_trailing_whitespace&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;insert_final_newline&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;root&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;语言特定属性
    &lt;ul&gt;
      &lt;li&gt;所有 Visual Studio 支持的语言（XML 除外）均支持 EditorConfig 编辑器设置。&lt;/li&gt;
      &lt;li&gt;此外，EditorConfig 还支持适用于 C# 和 Visual Basic 的代码样式约定和命名约定。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;也就是说，当你的项目中存在 EditorConfig 的配置文件 .editorconfig 的时候，Visual Studio 就会应用 EditorConfig 的设置，而且可以适用于多数情况下的编程约定。&lt;/p&gt;

&lt;p&gt;Visual Studio 中 .NET 相关语言（C# VB）的 EditorConfig 属性，可以参考 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017?wt.mc_id=MVP&quot;&gt;.NET coding convention settings For EditorConfig&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;在-visual-studio-中添加-editorconfig-配置&quot;&gt;在 Visual Studio 中添加 EditorConfig 配置&lt;/h2&gt;

&lt;p&gt;Visual Studio 支持 EditorConfig 对编程规范的约束。对于多数开发者来说，不需要安装任何插件的情况下这个编程规范的约束就会生效。&lt;/p&gt;

&lt;p&gt;不过，还是需要有一些小伙伴进行编程规范的设置。设置规范可以使用很多个插件，比如 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=MadsKristensen.EditorConfig&quot;&gt;EditorConfig Language Service&lt;/a&gt; 和 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.VSIntelliCode&quot;&gt;Visual Studio IntelliCode&lt;/a&gt;。当然，前者会更加专业，后者只是因为需要使用到 EditorConfig 的配置，顺便带上了 EditorConfig 的编辑体验。&lt;/p&gt;

&lt;p&gt;安装了 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=MadsKristensen.EditorConfig&quot;&gt;EditorConfig Language Service&lt;/a&gt; 插件之后，在解决方案上右键，添加 .editorconfig 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-10-58-33.png&quot; alt=&quot;添加 .editorconfig 文件&quot; /&gt;&lt;br /&gt;
▲ 添加 .editorconfig 文件&lt;/p&gt;

&lt;p&gt;当然，也许你会发现在我的图中，两个插件都能生成 .editorconfig 文件。EditorConfig Language Service 生成的 .editorconfig 文件是空的，而 IntelliCode 一经添加便提供了丰富的 C# 语言约定的属性设置。不过，IntelliCode 提供的设置多少取决于你目前解决方案中的项目类型，这些属性是从 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017?wt.mc_id=MVP&quot;&gt;这里&lt;/a&gt; 推断的。&lt;/p&gt;

&lt;p&gt;如果你使用 EditorConfig Language Service 生成了 .editorconfig 文件，则可以继续点击小灯泡生成按照微软约定的编程规范：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-11-10-20.png&quot; alt=&quot;生成规范&quot; /&gt;&lt;br /&gt;
▲ 生成规范&lt;/p&gt;

&lt;h2 id=&quot;在-visual-studio-中开启-editorconfig-支持&quot;&gt;在 Visual Studio 中开启 EditorConfig 支持&lt;/h2&gt;

&lt;p&gt;实际上，Visual Studio 一旦检测到 .editorconfig 文件的存在，格式约定就会自动生效。&lt;/p&gt;

&lt;h2 id=&quot;在-resharper-中开启-editorconfig-支持&quot;&gt;在 ReSharper 中开启 EditorConfig 支持&lt;/h2&gt;

&lt;p&gt;一样的，ReSharper 默认是开启了 EditorConfig 配置的检测的，也就是说只要存在 .editorconfig 文件，那么 EditorConfig 也会在 ReSharper 的格式化中生效。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-20-11-46-25.png&quot; alt=&quot;ReSharper 中的 EditorConfig 配置支持&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ReSharper 对于 EditorConfig 的支持情况可以参考：&lt;a href=&quot;https://www.jetbrains.com/help/resharper/Using_EditorConfig.html&quot;&gt;Using EditorConfig - Help - ReSharper&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;效果体验&quot;&gt;效果体验&lt;/h2&gt;

&lt;p&gt;我们来看一段风格十分混乱不忍直视的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;logger2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managerTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; 
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;managerTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;无论你是使用什么方式，最终都能格式化成下面这样：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;你可以直接输入，在遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;}&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 的时候就会格式化&lt;/li&gt;
  &lt;li&gt;你可以 Ctrl+V 粘贴，粘贴后直接就是格式化后的代码&lt;/li&gt;
  &lt;li&gt;你可以按下 Ctrl+Alt+Enter（ReSharper），这样整份文档就会格式化&lt;/li&gt;
  &lt;li&gt;你可以按下 Ctrl+K, D（Visual Studio 的 Cleanup），这样也能格式化&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managerTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;managerTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;附-editorconfig-language-service-生成的属性集&quot;&gt;附 EditorConfig Language Service 生成的属性集&lt;/h3&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[*]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;end_of_line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;crlf&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;charset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf-8-bom&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;indent_size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;insert_final_newline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;tab_width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;trim_trailing_whitespace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[*.xml]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;indent_style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;space&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[*.{cs,vb}]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_sort_system_directives_first&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_coalesce_expression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_collection_initializer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_explicit_tuple_names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_null_propagation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_object_initializer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_parentheses_in_arithmetic_binary_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always_for_clarity:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_parentheses_in_other_binary_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always_for_clarity:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_parentheses_in_other_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;never_if_unnecessary:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_parentheses_in_relational_binary_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always_for_clarity:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_predefined_type_for_locals_parameters_members&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_predefined_type_for_member_access&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_auto_properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_conditional_expression_over_assignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_conditional_expression_over_return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_inferred_anonymous_type_member_names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_inferred_tuple_names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_prefer_is_null_check_over_reference_equality_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_readonly_field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;dotnet_style_require_accessibility_modifiers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;for_non_interface_members:silent&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[*.cs]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_indent_case_contents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_indent_labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flush_left&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_indent_switch_labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_finally&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_members_in_anonymous_types&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_members_in_object_initializers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_open_brace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_new_line_between_query_expression_clauses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_preferred_modifier_order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_prefer_braces&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_prefer_simple_default_expression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_preserve_single_line_blocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_preserve_single_line_statements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_after_cast&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_after_colon_in_inheritance_clause&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_after_keywords_in_control_flow_statements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_around_binary_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;before_and_after&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_before_colon_in_inheritance_clause&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_empty_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_name_and_opening_parenthesis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_declaration_empty_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_declaration_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_space_between_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_conditional_delegate_call&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_deconstructed_variable_declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_accessors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_constructors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_indexers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_methods&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_inlined_variable_declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_pattern_local_over_anonymous_function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_pattern_matching_over_as_with_null_check&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_pattern_matching_over_is_with_cast_check&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_throw_expression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_var_elsewhere&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_var_for_built_in_types&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;csharp_style_var_when_type_is_apparent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:silent&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[*.vb]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;visual_basic_preferred_modifier_order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion:suggestion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;附-intellicode-生成的属性集&quot;&gt;附 IntelliCode 生成的属性集&lt;/h3&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\lvyi\Walterlv.Demo codebase based on best match to current usage at 2018/11/20
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[*.cs]&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Core editorconfig formatting - indentation
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#use soft tabs (spaces) for indentation
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;indent_style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;space&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Formatting - indentation options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#indent switch case contents.
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_indent_case_contents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#csharp_indent_case_contents_when_block
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_indent_case_contents_when_block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#indent switch labels
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_indent_switch_labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Formatting - new line options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#place catch statements on a new line
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#place else statements on a new line
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#require finally statements to be on a new line after the closing brace
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_finally&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#require braces to be on a new line for methods, accessors, control_blocks, lambdas, properties, and types (also known as &quot;Allman&quot; style)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_new_line_before_open_brace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;methods, accessors, control_blocks, lambdas, properties, types&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Formatting - organize using options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#sort System.* using directives alphabetically, and place them before other usings
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_sort_system_directives_first&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Formatting - spacing options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#require a space before the colon for bases or interfaces in a type declaration
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_after_colon_in_inheritance_clause&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#require a space after a keyword in a control flow statement such as a for loop
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_after_keywords_in_control_flow_statements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#require a space before the colon for bases or interfaces in a type declaration
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_before_colon_in_inheritance_clause&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#remove space within empty argument list parentheses
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_empty_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#remove space between method call name and opening parenthesis
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_name_and_opening_parenthesis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_call_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#remove space within empty parameter list parentheses for a method declaration
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_declaration_empty_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_space_between_method_declaration_parameter_list_parentheses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Formatting - wrapping options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#leave code block on single line
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_preserve_single_line_blocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#leave statements and member declarations on the same line
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_preserve_single_line_statements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Style - expression bodied member options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer block bodies for accessors
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_accessors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer block bodies for constructors
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_constructors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer block bodies for indexers
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_indexers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer block bodies for methods
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_methods&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer block bodies for properties
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_expression_bodied_properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Style - expression level options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer out variables to be declared before the method call
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_inlined_variable_declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_predefined_type_for_member_access&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Style - implicit and explicit types
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer var is used to declare variables with built-in system types such as int
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_var_for_built_in_types&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer var when the type is already mentioned on the right-hand side of a declaration expression
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;csharp_style_var_when_type_is_apparent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Style - language keyword and framework type options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_predefined_type_for_locals_parameters_members&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true:suggestion&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#Style - qualification options
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer events not to be prefaced with this. or Me. in Visual Basic
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer fields not to be prefaced with this. or Me. in Visual Basic
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer methods not to be prefaced with this. or Me. in Visual Basic
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#prefer properties not to be prefaced with this. or Me. in Visual Basic
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dotnet_style_qualification_for_property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false:suggestion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2017?wt.mc_id=MVP&quot;&gt;Using EditorConfig settings in Visual Studio - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017#formatting-conventions?wt.mc_id=MVP&quot;&gt;.NET coding convention settings For EditorConfig - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/editor-config-for-visual-studio.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/editor-config-for-visual-studio.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 Windows 10 中的加速度计（Accelerometer，重力传感器）</title>
        <description>&lt;p&gt;在做 UWP 应用开发的时候还有什么理由可以用到加速度计呢？场景很多啦，比如做游戏，做类似 Surface Hub 那种一边旋转，一边所有内容跟着一起转的效果。&lt;/p&gt;

&lt;p&gt;Windows 10 UWP 中的加速度计使用非常简单，只需要简单几句代码即可。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重力迷宫游戏&quot;&gt;重力迷宫游戏&lt;/h2&gt;

&lt;p&gt;这里有一个利用加速度计的好玩的例子：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-playing-gravity-maze-with-lumia950xl.gif&quot; alt=&quot;用 Lumia 950XL 玩重力迷宫&quot; /&gt;&lt;br /&gt;
▲ 用 Lumia 950XL 玩重力迷宫&lt;/p&gt;

&lt;p&gt;画质太渣了？确实太渣了。那就看看桌面版吧…… 反正是 UWP，两边看起来是一样的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-walterlv-gravity-maze.gif&quot; alt=&quot;重力迷宫桌面版画面（高清版）&quot; /&gt;&lt;br /&gt;
▲ 重力迷宫桌面版画面（高清版）&lt;/p&gt;

&lt;h2 id=&quot;初始化-accelerometer&quot;&gt;初始化 Accelerometer&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Accelerometer&lt;/code&gt; 在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.Devices.Sensors&lt;/code&gt; 命名空间下，使用时需要在类顶部加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.Devices.Sensors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而获得加速度计的实例只需要一句话：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Accelerometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果设备上没有加速度计，那么这里拿到的实例就会是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。所以注意需要进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 判断，毕竟大部分 Windows 10 设备都是普通电脑，没有加速度计的。&lt;/p&gt;

&lt;p&gt;现在，我们对加速度计进行一些简单的初始化：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Accelerometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 设置加速度计读数的报告间隔。这里我们与 16ms 进行判断，如果小于 16ms 就设为 16ms。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 因为我们在做游戏，帧数就是 60Hz，也就是说，我们不需要更高的读数间隔。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minReportInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MinimumReportInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reportInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minReportInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minReportInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReportInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reportInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 监听 ReadingChanged 事件，以便在加速度计读数改变时做一些操作。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadingChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Accelerometer_ReadingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;得到-accelerometer-的读数&quot;&gt;得到 Accelerometer 的读数&lt;/h2&gt;

&lt;p&gt;在监听事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Accelerometer_ReadingChanged&lt;/code&gt; 事件中，我们可以得到加速度计的读数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_xAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_yAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_zAxis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Accelerometer_ReadingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Accelerometer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccelerometerReadingChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AccelerometerReading&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_xAxis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccelerationX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_yAxis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccelerationY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_zAxis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccelerationZ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这些读数是 -1 到 1 之间的数值。&lt;/p&gt;

&lt;h2 id=&quot;将-accelerometer-的读数转化成倾斜角度&quot;&gt;将 Accelerometer 的读数转化成倾斜角度&lt;/h2&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/game-loop-of-win2d-canvas-animated-control&quot;&gt;Win2D 中的游戏循环：CanvasAnimatedControl&lt;/a&gt; 一文中，我在 PC 上玩这款游戏，也是在模拟桌子的倾角。于是我们也需要将读数转化成 Windows 10 设备的倾斜角度。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xAngle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yAngle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTiltAngles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_accelerometer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 从加速度计中读取读数，然后转换成设备倾斜角度。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_yAxis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_xAxis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果没有加速度计，则从键盘获得模拟的倾斜角度。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTiltAnglesByKeyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_xAxis&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;_yAxis&lt;/code&gt; 就是前面在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Accelerometer_ReadingChanged&lt;/code&gt; 事件中获得的读数数值。&lt;/p&gt;

&lt;p&gt;这里计算所得的角度值是下面图片中所指示的角度值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-21-22-55.png&quot; alt=&quot;X 方向数值&quot; /&gt;&lt;br /&gt;
▲ X 方向数值&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-21-23-00.png&quot; alt=&quot;Y 方向数值&quot; /&gt;&lt;br /&gt;
▲ Y 方向数值&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/use-the-accelerometer?wt.mc_id=MVP&quot;&gt;Use the accelerometer - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/uwp-accelerometer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/uwp-accelerometer.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 在代码中测量代码执行耗时的建议（比较系统性能计数器和系统时间）</title>
        <description>&lt;p&gt;我们有很多种方法评估一个方法的执行耗时，比如使用性能分析工具，使用基准性能测试。不过传统的在代码中编写计时的方式依然有效，因为它可以生产环境或用户端得到真实环境下的执行耗时。&lt;/p&gt;

&lt;p&gt;如果你希望在 .NET/C# 代码中编写计时，那么阅读本文可以获得一些建议。阅读本文也可以了解到 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueryPerformanceCounter&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­System­Time­As­File­Time&lt;/code&gt; 等方法的差异。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;基本的计时&quot;&gt;基本的计时&lt;/h2&gt;

&lt;p&gt;计时一般采用下面这种方式，在方法执行之前获取一次时间，在方法结束之后再取得一次时间。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 在方法开始之前。&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 在方法执行之后。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，前后两次获取的时间差即为方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的执行耗时。&lt;/p&gt;

&lt;p&gt;这里我不会提到性能测试工具或者基准性能测试这些方法，因为这些测试代码不会运行于用户端。你可以阅读以下博客获得这两者的使用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%A0%87%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.html&quot;&gt;C# 标准性能测试 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%A0%87%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95.html&quot;&gt;C# 标准性能测试高级用法 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet-high-performance-reflection-suggestions&quot;&gt;.NET/C# 反射的的性能数据，以及高性能开发建议（反射获取 Attribute 和反射调用方法） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;结论使用什么方法计时&quot;&gt;结论：使用什么方法计时&lt;/h2&gt;

&lt;p&gt;先说结论：&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Diagnostics&lt;/code&gt; 命名空间下有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; 类。如果你要为你方法的执行时间进行统计，那么就使用这个类。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; 类有一些静态属性、也有一些实例方法和实例属性。此类型的时间统计是按照高性能和高精度的要求来做的，于是你可以用它获得高精度的计时效果。不过，如果你对性能要求近乎苛刻，例如你的方法会被数百万次或更高频地执行，那么就需要开始斟酌如何调用里面的属性了。&lt;/p&gt;

&lt;p&gt;简单的使用如下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartNew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，你也可以直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; 的构造函数，&lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来之后再 &lt;code class=&quot;highlighter-rouge&quot;&gt;Start&lt;/code&gt;，不过 &lt;code class=&quot;highlighter-rouge&quot;&gt;StartNew&lt;/code&gt; 静态方法可以将两句合并为一句。&lt;/p&gt;

&lt;h2 id=&quot;各种计时-api-及其比较&quot;&gt;各种计时 API 及其比较&lt;/h2&gt;

&lt;p&gt;计时还有很多的方法，你可以针对不同需求场景使用不同的方法。不过，如果你根本没有了解过其他方法的话，那么建议直接使用上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt;，不要想太多。&lt;/p&gt;

&lt;p&gt;现在，我们看看 Windows 下的计时还有哪些 API：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;基于 QPC 的高精度 API
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Performance­Counter&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Performance­Frequency&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;基于系统时间的非高精度 API
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­Tick­Count&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Get­Tick­Count64&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­Message­Time&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­System­Time&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Get­Local­Time&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Get­System­Time­As­File­Time&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Interrupt­Time&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Unbiased­Interrupt­Time&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;基于 QPC 和系统时间的 API
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­System­Time­Precise­As­File­Time&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Interrupt­Time­Precise&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Query­Unbiased­Interrupt­Time­Precise&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;基于系统性能计数器qpc的-api&quot;&gt;基于系统性能计数器（QPC）的 API&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;QueryPerformanceCounter&lt;/code&gt;，微软文档中把它称之为 QPC。&lt;/p&gt;

&lt;p&gt;一般情况下使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;QueryPerformanceCounter&lt;/code&gt;，内核驱动开发者使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeQueryPerformanceCounter&lt;/code&gt; 和 .NET 开发者使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Diagnostics.Stopwatch&lt;/code&gt; 都是基于 QPC 的 API。&lt;/p&gt;

&lt;p&gt;QPC 是通过计算机上独立运行的高精度硬件计时模块来获得时间戳的。这意味着，使用此 API 获得的时间戳是本机时间戳，不包含任何时区等信息。&lt;/p&gt;

&lt;p&gt;由于 QPC 的高精度特性，所以非常适合在单个设备上测量一个小段时间的时间间隔。而这也符合我们本文一开始说到的方法执行耗时测量需求。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;QueryPerformanceCounter&lt;/code&gt; 得到的值是 Ticks，单位是 100 ns。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1 tick  = 100 ns
1 us    = 1000 ns
1 ms    = 1000 us
1 s     = 1000 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;基于系统时间的-api&quot;&gt;基于系统时间的 API&lt;/h3&gt;

&lt;p&gt;如果你的需求不止是测量获取一个时间间隔，而是需要一个长期保存的时间，或者需要将时间与其他设备进行通信，那么基于单台设备的 QPC 就不符合要求了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetSystemTimeAsFileTime&lt;/code&gt; 可以用来获取系统时钟时间。这个时间就是基于系统时钟的，所以如果你的时间戳是用来通信的，那么就很有用。当然，如果要在设备之间进行与时间信息相关的同步，还可能需要使用 NTP（Network Time Protocol）先同步时间。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; 获取时间的方法就是这个：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MethodImplAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MethodImplOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InternalCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSystemTimeAsFileTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里有一些比较有趣的说法，基于系统时间的 API 也会说成是获取高精度时间，那么跟 QPC 有什么不同呢？&lt;/p&gt;

&lt;p&gt;这里我只能拿英文来说话了。来自微软的 Raymond Chen 在它的 &lt;a href=&quot;https://www.amazon.com/gp/product/0321440307?ie=UTF8&amp;amp;tag=tholneth-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0321440307&quot;&gt;The Old New Thing&lt;/a&gt; 一书中说，基于系统时间的 API 获取的时间戳精度用的是 “所谓的 Precise”，但实际上应该称之为 “Accurate”，而 QPC 才能称之为实质上的 “Precise”。纠结起来就是 QPC 比基于系统时间的 API 得到的时间戳精度更高。&lt;/p&gt;

&lt;h3 id=&quot;基于-qpc-和系统时间的-api&quot;&gt;基于 QPC 和系统时间的 API&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get­System­Time­Precise­As­File­Time&lt;/code&gt; 这些 API 既可以获得 QPC 的高精度，又与系统时钟相关，于是你可以使用这些 API 同时获得以上测量的好处。当然，这以性能成本为代价的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/sysinfo/acquiring-high-resolution-time-stamps?wt.mc_id=MVP&quot;&gt;Acquiring high-resolution time stamps - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/oldnewthing/20170921-00/?p=97057&quot;&gt;How accurate are the various Windows time-querying functions? – The Old New Thing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/kex1n/p/3297607.html&quot;&gt;windows平台时间函数性能比较QueryPerformanceCounter，GetTickCount，ftime，time,GetLocalTime，GetSystemTimeAsFileTime - 小 楼 一 夜 听 春 雨 - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/28648/6233938&quot;&gt;c# - Is DateTime.Now the best way to measure a function’s performance? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/10107140/6233938&quot;&gt;c# - How do I measure how long a function is running? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/14019510/6233938&quot;&gt;c# - Calculate the execution time of a method - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch.ishighresolution?redirectedfrom=MSDN&amp;amp;view=netframework-4.7.2?wt.mc_id=MVP&quot;&gt;Stopwatch.IsHighResolution Field (System.Diagnostics) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,ceb0ba9cc88de82e&quot;&gt;Stopwatch.cs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/timespan.cs,865ef7b89f41b632&quot;&gt;timespan.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-high-precision-performance-counting.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-high-precision-performance-counting.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 反射的的性能数据，以及高性能开发建议（反射获取 Attribute 和反射调用方法）</title>
        <description>&lt;p&gt;大家都说反射耗性能，但是到底有多耗性能，哪些反射方法更耗性能；这些问题却没有统一的描述。&lt;/p&gt;

&lt;p&gt;本文将用数据说明反射各个方法和替代方法的性能差异，并提供一些反射代码的编写建议。为了解决反射的性能问题，你可以遵循本文采用的各种方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;反射各方法的性能数据&quot;&gt;反射各方法的性能数据&lt;/h2&gt;

&lt;p&gt;我使用 &lt;a href=&quot;https://benchmarkdotnet.org/&quot;&gt;BenchmarkDotNet&lt;/a&gt; 基准性能测试来评估反射各个方法的性能。测试的程序基于 .NET Core 2.1 开发。&lt;/p&gt;

&lt;p&gt;先直观地贴出我的运行结果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-01-05.png&quot; alt=&quot;各反射不同方法的运行基准测试结果&quot; /&gt;&lt;br /&gt;
▲ 各反射不同方法的运行基准测试结果&lt;/p&gt;

&lt;p&gt;我把上面的表格复制下来成为文字，这样你也可以拿走我的这部分数据：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Mean&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Error&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;StdDev&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Median&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Assembly&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.5315 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.3004 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.4764 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.4878 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Attributes&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7.0893 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.1248 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.1168 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7.0982 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CustomAttributes&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,489.1654 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;29.4428 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;27.5408 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,482.5038 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttributesData&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,514.5503 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;29.6863 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;39.6303 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,507.2949 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttributes&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,171.8969 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;22.5305 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;27.6695 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,167.2777 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttribute&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,139.8609 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;22.8043 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;24.4003 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,140.5437 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttribute_Generic&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,115.0049 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.1929 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;11.6952 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,111.4426 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttributes_Generic&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,164.5132 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;22.7775 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;24.3716 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,165.2747 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;New&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0003 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0013 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0012 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0000 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lambda&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0063 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0149 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0139 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.0000 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Activator_CreateInstance&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;48.8633 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.6300 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.5893 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;48.8906 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Activator_CreateInstance_Generic&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;47.7029 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.9649 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.0724 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;47.5851 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Expression_New&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;75,634.4035 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,467.3285 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,372.5400 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;75,413.2837 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CachedExpression_New&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7.8923 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.1988 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.4105 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7.7004 ns&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;如果你希望了解以上每一项的意思，可以通过阅读本文文末的代码来了解其实现。基本上名称就代表着反射调用相同的方法。&lt;/p&gt;

&lt;p&gt;你一定会说这张表不容易看出性能差距。那么我一定会放图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-21-13.png&quot; alt=&quot;性能差异图 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Expression_New&lt;/code&gt; 在图中独树一帜，远远把其他方法甩在了后面。那是个什么方法？&lt;/p&gt;

&lt;p&gt;那是在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Expression&lt;/code&gt; 表达式创建一个类型的新实例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，如果你只是希望创建一个类型的新实例，就不要考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Expression.New&lt;/code&gt; 的方式了。除非此方法将执行非常多次，而你把那个 lambda 表达式缓存下来了。这对应着图表中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CachedExpression_New&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;其他的现在都看不出来性能差异，于是我们把耗时最长的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Expression_New&lt;/code&gt; 一项去掉：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-25-39.png&quot; alt=&quot;性能差异图 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们立刻可以从图中得到第二梯队的性能巨头 —— 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomAttributes&lt;/code&gt; 系列。我使用了多种不同的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomAttribute&lt;/code&gt; 获取方法，得到的结果差异不大，都“比较耗时”。不过在这些耗时的方法里面找到不那么耗时的，就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type&lt;/code&gt; 的扩展方法系列 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 了，比原生非扩展方法的性能稍好。&lt;/p&gt;

&lt;p&gt;不过其他的性能差异又被淹没了。于是我们把 &lt;code class=&quot;highlighter-rouge&quot;&gt;CustomAttributes&lt;/code&gt; 系列也删掉：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-28-55.png&quot; alt=&quot;性能差异图 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是我们又得到了第三梯队的性能大头 —— &lt;code class=&quot;highlighter-rouge&quot;&gt;Activator.CreateInstance&lt;/code&gt; 系列。而是否调用泛型方法的耗时差异不大。&lt;/p&gt;

&lt;p&gt;然后，我们把 &lt;code class=&quot;highlighter-rouge&quot;&gt;Activator.CreateInstance&lt;/code&gt; 也干掉，可以得到剩下其他的性能消耗。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-30-53.png&quot; alt=&quot;性能差异图 4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;也就是说，只是获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type&lt;/code&gt; 中的一些属性，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attributes&lt;/code&gt; 也是比较“耗时”的；当然，这是纳秒级别，你可以将它忽略。&lt;/p&gt;

&lt;p&gt;要不要试试把第四梯队的也干掉呢？于是你可以得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lambda&lt;/code&gt; 的差异：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-03-14-34-30.png&quot; alt=&quot;性能差异图 5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原本在上面所有图中看起来都没有时间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lambda&lt;/code&gt; 竟然差异如此巨大；不过，这都是千分之一纳秒级别了；如果你创建的类数量不是百万级别以上，你还真的可以忽略。&lt;/p&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 指的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;new Foo()&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Lambda&lt;/code&gt; 指的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;var func = () =&amp;gt; new Foo(); func();&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt;，还有另一个方法值得注意：&lt;code class=&quot;highlighter-rouge&quot;&gt;IsDefined&lt;/code&gt;；可以用来判断是否定义了某个特定的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isDefined&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而这个方法与 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 的性能差距也有些大：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Mean&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Error&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;StdDev&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Ratio&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;RatioSD&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;IsDefined&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;653.8 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.07 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;16.53 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.00&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetCustomAttribute&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,149.6 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;22.97 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;22.56 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.76&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.06&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetGenericCustomAttribute&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,216.5 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;24.15 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;54.51 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.81&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.07&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;咋看之下似乎与 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 方法重复，而且如果先判断再获取，可能总时间更长。不过这种方法就是适用于一次性对大量类型进行判断，如果只有少量类型定义了某种 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，那么提前使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsDefined&lt;/code&gt; 判断可以获得总体更加的性能。&lt;/p&gt;

&lt;h2 id=&quot;反射的高性能开发建议&quot;&gt;反射的高性能开发建议&lt;/h2&gt;

&lt;h3 id=&quot;创建类型的实例&quot;&gt;创建类型的实例&lt;/h3&gt;

&lt;p&gt;如果你能访问到类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;建议直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt;，性能最好。&lt;/li&gt;
  &lt;li&gt;如果不希望直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来，可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Func&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&lt;/code&gt; 创建。这时会多消耗一些性能，不过基数小，增量不大。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你不能访问到类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果只能从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Type&lt;/code&gt; 创建，则使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Activator.CreateInstance&lt;/code&gt; 系列。&lt;/li&gt;
  &lt;li&gt;如果你使用其他方式创建，请一定使用缓存。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除了使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Expression&lt;/code&gt; 创建，你还可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Emit&lt;/code&gt; 创建，不过这也要求能够访问到类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/generate-il-using-emit&quot;&gt;使用 Emit 生成 IL 代码 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于缓存，可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-delegate-to-improve-reflection-performance&quot;&gt;.NET Core/Framework 创建委托以大幅度提高反射调用的性能 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-cache-pool&quot;&gt;.NET/C# 推荐一个我设计的缓存类型（适合缓存反射等耗性能的操作，附用法） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于创建对象更多的性能数据，可以参考：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E7%9B%B4%E6%8E%A5%E5%88%9B%E5%BB%BA%E5%A4%9A%E4%B8%AA%E7%B1%BB%E5%92%8C%E4%BD%BF%E7%94%A8%E5%8F%8D%E5%B0%84%E5%88%9B%E5%BB%BA%E7%B1%BB%E7%9A%84%E6%80%A7%E8%83%BD.html&quot;&gt;C# 直接创建多个类和使用反射创建类的性能 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90-%E5%8F%8D%E5%B0%84-VS-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-VS-%E9%A2%84%E7%BC%96%E8%AF%91.html&quot;&gt;C# 性能分析 反射 VS 配置文件 VS 预编译 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;反射获取-attribute&quot;&gt;反射获取 Attribute&lt;/h3&gt;

&lt;p&gt;获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 也是耗时的操作。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果你只是获取极少数类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，建议直接调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 扩展方法。&lt;/li&gt;
  &lt;li&gt;如果你需要判断大量类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，建议先使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsDefined&lt;/code&gt; 判断是否存在，如果存在才使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCustomAttribute&lt;/code&gt; 方法获取真实实例。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;反射调用公共--私有方法&quot;&gt;反射调用公共 / 私有方法&lt;/h3&gt;

&lt;p&gt;反射调用方法与构造方法几乎是一样的，不同之处就在于公共方法可以创建出委托缓存，而私有方法却不行。&lt;/p&gt;

&lt;p&gt;有了委托缓存，你只有第一次才需要真的调用反射，后续可以使用缓存的委托或 Lambda 表达式；而私有方法是无法创建的，你每次都需要通过反射来调用相关方法。&lt;/p&gt;

&lt;p&gt;关于私有方法的反射：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E4%BD%BF%E7%94%A8%E5%8F%8D%E5%B0%84%E8%8E%B7%E5%8F%96%E7%A7%81%E6%9C%89%E5%B1%9E%E6%80%A7%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;C# 使用反射获取私有属性的方法&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-%E5%8F%8D%E5%B0%84%E8%B0%83%E7%94%A8%E7%A7%81%E6%9C%89%E4%BA%8B%E4%BB%B6.html&quot;&gt;C# 反射调用私有事件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于缓存：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-delegate-to-improve-reflection-performance&quot;&gt;.NET Core/Framework 创建委托以大幅度提高反射调用的性能 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/design-a-cache-pool&quot;&gt;.NET/C# 推荐一个我设计的缓存类型（适合缓存反射等耗性能的操作，附用法） - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;使用预编译框架&quot;&gt;使用预编译框架&lt;/h3&gt;

&lt;p&gt;使用预编译框架，你可以在编译期间将那些耗时的反射操作编译成类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 和属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;get&lt;/code&gt; 这样的简单 CLR 调用，性能差距近乎于最开始图表中第二张图和第五张图那样，具有数千倍的差距。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet-build-and-roslyn-course-in-tech-summit-2018&quot;&gt;课程 预编译框架，开发高性能应用 - 微软技术暨生态大会 2018 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet-campus/SourceFusion&quot;&gt;dotnet-campus/SourceFusion: SourceFusion is a pre-compile framework based on Roslyn. It helps you to build high-performance .NET code.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;附本文性能测试所用的代码&quot;&gt;附本文性能测试所用的代码&lt;/h2&gt;

&lt;p&gt;本文性能测试使用 &lt;a href=&quot;https://benchmarkdotnet.org/&quot;&gt;BenchmarkDotNet&lt;/a&gt;，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数中调用以下代码跑起来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BenchmarkRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reflections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%A0%87%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.html&quot;&gt;C# 标准性能测试 - 林德熙&lt;/a&gt; 了解基准性能测试的基本用法，在 &lt;a href=&quot;https://blog.lindexi.com/post/C-%E6%A0%87%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95.html&quot;&gt;C# 标准性能测试高级用法 - 林德熙&lt;/a&gt; 中了解到更多基准测试方法的使用。&lt;/p&gt;

&lt;h3 id=&quot;所有反射相关方法&quot;&gt;所有反射相关方法&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;BenchmarkDotNet.Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq.Expressions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Reflection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Reflections&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cachedExpressionFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedExpressionFunc&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cachedExpressionFunc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_cachedExpressionFunc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cachedExpressionFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CustomAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CustomAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttributesData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttribute_Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttributes_Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Activator_CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Activator_CreateInstance_Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Expression_New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CachedExpression_New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedExpressionFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;isdefined-和-getcustomattribute-的专项比较&quot;&gt;IsDefined 和 GetCustomAttribute 的专项比较&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;BenchmarkDotNet.Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Reflection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IsDefinedVsGetCustomAttribute&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Baseline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isDefined&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetGenericCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_targetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReflectionTargetAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/14719740/6233938&quot;&gt;c# - Is there a benefit of using IsDefined over GetCustomAttributes - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/accessing-attributes-by-using-reflection?wt.mc_id=MVP&quot;&gt;Accessing Attributes by Using Reflection (C#) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E5%8F%8D%E5%B0%84.html&quot;&gt;win10 uwp 反射&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/rttype.cs,a4aa0f217732eb81&quot;&gt;Reference Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://geekswithblogs.net/mrsteve/archive/2012/02/19/a-fast-c-sharp-extension-method-using-expression-trees-create-instance-from-type-again.aspx&quot;&gt;A Super-Fast C# Extension Method using Expression Trees to Create an instance from a Type&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://scottdorman.github.io/2010/05/16/retrieving-custom-attributes-using-reflection/&quot;&gt;Retrieving Custom Attributes Using Reflection - Scott Dorman&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://benchmarkdotnet.org/&quot;&gt;Showtime - BenchmarkDotNet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://benchmarkdotnet.org/articles/guides/choosing-run-strategy.html&quot;&gt;Choosing RunStrategy - BenchmarkDotNet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-high-performance-reflection-suggestions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-high-performance-reflection-suggestions.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 异常处理：写一个空的 try 块代码，而把重要代码写到 finally 中（Constrained Execution Regions）</title>
        <description>&lt;p&gt;不知你是否见过 &lt;code class=&quot;highlighter-rouge&quot;&gt;try { } finally { }&lt;/code&gt; 代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块留空，而只往 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 中写代码的情况呢？这种写法有其特殊的目的。&lt;/p&gt;

&lt;p&gt;本文就来说说这种不一样的写法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;空的-try-块&quot;&gt;空的 try 块&lt;/h2&gt;

&lt;p&gt;你可以点开&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/src/System/Exception.cs,a445c4e8ae46b283,references&quot;&gt;这个链接&lt;/a&gt;查看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 类，在里面你可以看到一段异常处理的代码非常奇怪：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 代码已经过简化。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RestoreExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略代码。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 省略代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;神奇之处就在于，其 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; 块是空的，重要代码都放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 中。那为什么会这么写呢？&lt;/p&gt;

&lt;p&gt;在代码注释中的解释为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We do this inside a &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; clause to ensure &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAbort&lt;/code&gt; cannot be injected while we have taken the lock. This is to prevent unrelated exception restorations from getting blocked due to TAE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;翻译过来是：&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 子句中执行此操作以确保在获取锁时无法注入 &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAbort&lt;/code&gt;。这是为了防止不相关的异常恢复因 TAE 而被阻止。&lt;/p&gt;

&lt;p&gt;也就是说，&lt;strong&gt;此方法是为了与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Abort&lt;/code&gt; 对抗，防止 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Abort&lt;/code&gt; 中断此处代码的执行。&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Abort&lt;/code&gt; 的执行交给 CLR 管理，&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 的执行也是交给 CLR 管理。CLR 确保 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 块执行的时候不会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread.Abort&lt;/code&gt; 阻止。&lt;/p&gt;

&lt;p&gt;代码在 .NET Core 和 .NET Framework 中的实现完全一样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// exception, just before the exception is &quot;rethrown&quot;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RestoreExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fCanProcessException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsImmutableAgileException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Restore only for non-preallocated exceptions&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fCanProcessException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Take a lock to ensure only one thread can restore the details&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// at a time against this exception object that could have&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// multiple ExceptionDispatchInfo instances associated with it.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We do this inside a finally clause to ensure ThreadAbort cannot&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// be injected while we have taken the lock. This is to prevent&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// unrelated exception restorations from getting blocked due to TAE.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// When restoring back the fields, we again create a copy and set reference to them&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// in the exception object. This will ensure that when this exception is thrown and these&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// fields are modified, then EDI's references remain intact.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Since deep copying can throw on OOM, try to get the copies&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// outside the lock.&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stackTraceCopy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BinaryStackTraceArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeepCopyStackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BinaryStackTraceArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dynamicMethodsCopy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DynamicMethodArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeepCopyDynamicMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DynamicMethodArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            
            &lt;span class=&quot;c1&quot;&gt;// Finally, restore the information. &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s_EDILock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_watsonBuckets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WatsonBuckets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_ipForWatsonBuckets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPForWatsonBuckets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_remoteStackTraceString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoteStackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SaveStackTracesFromDeepCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stackTraceCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dynamicMethodsCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_stackTraceString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Marks the TES state to indicate we have restored foreign exception&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// dispatch information.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrepareForForeignExceptionRaise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以在 &lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/exception.cs,a445c4e8ae46b283&quot;&gt;这里&lt;/a&gt; 查看 .NET Framework 版本，在&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/src/System/Exception.cs,a445c4e8ae46b283,references&quot;&gt;这里&lt;/a&gt; 查看 .NET Core 的版本。&lt;/p&gt;

&lt;h2 id=&quot;受约束的执行区域constrained-execution-regions&quot;&gt;受约束的执行区域（Constrained Execution Regions）&lt;/h2&gt;

&lt;p&gt;这种现象在微软官方文档 &lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/framework/performance/reliability-best-practices#protect-critical-operations-with-constrained-execution-regions-and-reliability-contracts?wt.mc_id=MVP&quot;&gt;可靠性最佳做法&lt;/a&gt; 中有介绍。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Doing so instructs the just-in-time compiler to prepare all the code in the finally block before running the &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; block. This guarantees that the code in the finally block is built and will run in all cases. It is not uncommon in a CER to have an empty &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; block. Using a CER protects against asynchronous thread aborts and out-of-memory exceptions. See &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.runtimehelpers.executecodewithguaranteedcleanup?wt.mc_id=MVP&quot;&gt;ExecuteCodeWithGuaranteedCleanup&lt;/a&gt; for a form of a CER that additionally handles stack overflows for exceedingly deep code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 形成一个受约束的执行区域，使得 &lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 中的代码被可靠地执行。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/framework/performance/reliability-best-practices#protect-critical-operations-with-constrained-execution-regions-and-reliability-contracts?wt.mc_id=MVP&quot;&gt;可靠性最佳做法 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/framework/performance/constrained-execution-regions#noninterruptible-regions?wt.mc_id=MVP&quot;&gt;受约束的执行区域 - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/exception.cs,a445c4e8ae46b283&quot;&gt;exception.cs - Reference Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://source.dot.net/#System.Private.CoreLib/src/System/Exception.cs,a445c4e8ae46b283,references&quot;&gt;RestoreExceptionDispatchInfo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://web.archive.org/web/20130523155042/http://blog.somecreativity.com/2008/04/10/the-empty-try-block-mystery/&quot;&gt;The empty try block mystery - Some Creativity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/2186101/6233938&quot;&gt;c# - Why use try {} finally {} with an empty try block? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/corefx/blob/master/src/System.Runtime/ref/System.Runtime.cs&quot;&gt;corefx/System.Runtime.cs at master · dotnet/corefx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/empty-try-block.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/empty-try-block.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C# 重载条件逻辑运算符（&amp;&amp; 和 ||）</title>
        <description>&lt;p&gt;在微软的官方文档中，规定 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 运算符不可被重载，但允许通过重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 实现间接重载。&lt;/p&gt;

&lt;p&gt;本文将介绍重载方法和原理。感谢 &lt;a href=&quot;https://disqus.com/by/OpportunityLiu/&quot;&gt;Opportunity&lt;/a&gt; 的指导。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;条件逻辑运算符是可以重载的&quot;&gt;条件逻辑运算符是可以重载的&lt;/h2&gt;

&lt;p&gt;在微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/true-operator?wt.mc_id=MVP&quot;&gt;true Operator (C# Reference) - Microsoft Docs&lt;/a&gt; 中，解释了 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 这两个条件逻辑运算符的重载方法：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A type cannot directly overload the Caseal logical operators (&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt;), but an equivalent effect can be achieved by overloading the regular logical operators and operators &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;类型不能直接重载条件逻辑运算符（&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt;），但通过重载常规逻辑运算符 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; 及运算符 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 可以达到同样的效果。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，在官方的概念中，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 是允许被重载的，只是不能直接重载。&lt;/p&gt;

&lt;p&gt;原因在于，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 是短路运算符（Circuit Operator），具有短路求值特性。具体来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;A &amp;amp;&amp;amp; B&lt;/code&gt; 运算中，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; 的值便不会计算；同样的，&lt;code class=&quot;highlighter-rouge&quot;&gt;A || B&lt;/code&gt; 中，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; 的值也不会计算。&lt;/p&gt;

&lt;p&gt;于是，如果允许自定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 运算符，那么必然会导致这个运算符重载的方法有两个参数传入，于是这两个参数一定会被计算值；这样就无法实现短路求值了。于是对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 的重载采用的方案是重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; 运算符，然后重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 运算符来指定短路求值。&lt;/p&gt;

&lt;h2 id=&quot;试错实验&quot;&gt;试错实验&lt;/h2&gt;

&lt;p&gt;我们写一个类型进行实验：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 是没有问题的，但如果使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 就会提示错误。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Error CS0217: In order to be applicable as a short circuit operator a user-defined logical operator (‘Case.operator &amp;amp;(Case, Case)’) must have the same return type and parameter types&lt;/p&gt;

  &lt;p&gt;Error CS0217: 为了可以像短路运算符一样应用，用户定义的逻辑运算符(“Case.operator &amp;amp;(Case, Case)”)的返回类型和参数类型必须相同&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，本身重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 运算符的时候允许返回不同的类型；但如果希望 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 运算符在此重载下也生效，就必须确保 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 的返回类型与参数中的类型相同。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;改为相同的类型后，还会继续提示需要定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 运算符。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Error CS0218: In order for ‘Case.operator &amp;amp;(Case, Case)’ to be applicable as a short circuit operator, its declaring type ‘Case’ must define operator true and operator false&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;重载--和-&quot;&gt;重载 &amp;amp;&amp;amp; 和 ||&lt;/h2&gt;

&lt;p&gt;以下代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表示字符串中包含大写字母，&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 表示字符串中不包含大写字母（&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 和没有大小写的区域也属于不包含大写字母）。&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 运算符仅留下两者共有的字符；&lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; 则取所有字符。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;测试重载了条件逻辑运算符的类型&quot;&gt;测试重载了条件逻辑运算符的类型&lt;/h2&gt;

&lt;p&gt;我们测试以上代码所用的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a 是 truthy&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a 是 falsy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b 是 truthy&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b 是 falsy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上各个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine&lt;/code&gt; 的输出为：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[1] A
[2] b
[3] a 是 truthy
[4] b 是 falsy
[5] 
[6] Ab
[7] 
[8] A
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，空行其实指的是输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;truthy-和-falsy&quot;&gt;truthy 和 falsy&lt;/h2&gt;

&lt;p&gt;刚刚的测试代码中，我们使用了 truthy 和 falsy 概念，而这是逻辑判断概念：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果在逻辑判断中，对象与 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 等价，但其数值上并非 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;（不等于 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;），那么称此对象为 truthy；&lt;/li&gt;
  &lt;li&gt;如果在逻辑判断中，对象与 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 等价，但其数值上并非 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;（不等于 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;），那么称此对象为 falsy。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;对以上测试输出的解释&quot;&gt;对以上测试输出的解释&lt;/h2&gt;

&lt;p&gt;第 5 行由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 没有共有字符，所以得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;第 7 行的执行过程是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 求值，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 本身；&lt;/li&gt;
  &lt;li&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 进行 truthy / falsy 逻辑判断，得到 truthy；&lt;/li&gt;
  &lt;li&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 为 truthy，对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 运算符而言，可以对 b 求值，于是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 求值得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 本身；&lt;/li&gt;
  &lt;li&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; 运算，得到 ` &lt;code class=&quot;highlighter-rouge&quot;&gt;，也就是 &lt;/code&gt;null`。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;第 8 行的执行过程是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 求值，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 本身；&lt;/li&gt;
  &lt;li&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 进行 truthy / falsy 逻辑判断，得到 truthy；&lt;/li&gt;
  &lt;li&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 为 truthy，对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt; 运算符而言，已无需对 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 求值，最终得到的结果为 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/overridable-operators-in-csharp.html#comment-4147325525&quot;&gt;C# 中那些可以被重载的操作符 - walterlv - 请阅读文章末尾的评论&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/true-operator?wt.mc_id=MVP&quot;&gt;true Operator (C# Reference) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://amobiz.github.io/2015/09/28/javascript-truthy-falsy/&quot;&gt;JavaScript: Truthy? Falsy? - 格物致知&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/overload-conditional-and-and-or-operators-in-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/overload-conditional-and-and-or-operators-in-csharp.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 WPF 中使用 x:Reference</title>
        <description>&lt;p&gt;x:Reference 是 XAML 2009 中引入的功能，也算是比较早的功能了；ElementName 是 XAML 一开始出现便开始有的功能。二者在使用时在感觉上是比较相似的，但多数情况下都更有优势。&lt;/p&gt;

&lt;p&gt;本文将解释 x:Reference。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;典型的使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Reference&lt;/code&gt; 的例子是：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;object&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Reference instancexName}&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;instancexName&lt;/code&gt; 是另一个用 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Name&lt;/code&gt; 指定名称的元素。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x:Reference&lt;/code&gt; 前面带了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; 命名空间前缀，所以可想而知这是与 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Name&lt;/code&gt; 类似的 XAML 编译相关的标记。&lt;/p&gt;

&lt;p&gt;在微软官方文档中描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In WPF and XAML 2006, element references are addressed by the framework-level feature of ElementName binding. For most WPF applications and scenarios, ElementName binding should still be used. Exceptions to this general guidance might include cases where there are data context or other scoping considerations that make data binding impractical and where markup compilation is not involved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;用中文来描述就是说：以前在 XAML 2006 的时候，使用 ElementName 在绑定中获得对应到元素的绑定源，而这能适用于大多数情况。不过，如果绑定上下文中拥有不同的命名边界，那么这时使用 ElementName 可能无法找到绑定源。这时可以使用 x:Reference 替代。&lt;/p&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;/post/fix-wpf-binding-issues-in-context-menu&quot;&gt;WPF 的 ElementName 在 ContextMenu 中无法绑定成功？试试使用 x:Reference！ - walterlv&lt;/a&gt; 了解 x:Reference 替代 ElementName 解决绑定中命名边界的问题。&lt;/p&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;ElementName&lt;/code&gt; 是在运行时通过查找可视化树或逻辑树来确定名称边界（NameScope）的，所以一定程度上性能也不那么好。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/xaml-services/x-reference-markup-extension?wt.mc_id=MVP&quot;&gt;x:Reference Markup Extension - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/19244111/6233938&quot;&gt;wpf - What is the difference between x:Reference and ElementName? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/14644924/6233938&quot;&gt;binding - When is x:Reference in WPF resolved and why does XAML element order affect it? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://codeday.me/bug/20170930/78263.html&quot;&gt;wpf – x：Reference和ElementName之间有什么区别？ - 代码日志&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-x-reference.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-x-reference.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>UWP 轻量级样式定义（Lightweight Styling）</title>
        <description>&lt;p&gt;在 UWP 中，可以通过给控件直接设置属性或在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 中设置属性来定制控件的样式；不过这样的样式定义十分有限，比如按钮按下时的样式就没法儿设置。当然可以通过修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt; 来设置控件的样式，然而 UWP 中控件的样式代码实在是太多太复杂了，还不容易从 Blend 中复制了大量代码出来改，下个版本样式又不一样，于是我们就丢了不少功能。&lt;/p&gt;

&lt;p&gt;本文将介绍 UWP 轻量级样式定义（Lightweight styling），你既不用写太多代码，又能获得更多的样式控制。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;轻量级样式定义&quot;&gt;轻量级样式定义&lt;/h2&gt;

&lt;p&gt;看一段简单的代码，你一定能立刻明白本文想说的是什么。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary.ThemeDictionaries&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBackground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonForeground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#dd5145&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBorderBrush&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#dd5145&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary.ThemeDictionaries&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page.Resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本段代码摘抄自 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles#lightweight-styling?wt.mc_id=MVP&quot;&gt;XAML Lightweight styling - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-25-20-00-48.png&quot; alt=&quot;按钮的颜色定制&quot; /&gt;&lt;br /&gt;
▲ 按钮的颜色定制&lt;/p&gt;

&lt;p&gt;以上代码可以写在 Page 中，即可在 Page 范围内获得这些主题资源的重写。当然，如果需要更大范围，可以考虑去 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; 类中重写。&lt;/p&gt;

&lt;p&gt;官网上举例的这种类型的样式定义其实普通的 Style 也能很容易实现的，真正厉害的是 Style 里设置不了的那些鼠标滑过颜色和鼠标按下颜色。于是，我们额外添加一些代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBackground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonForeground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#dd5145&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBorderBrush&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#dd5145&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBackgroundPointerOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#10dd5145&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonForegroundPointerOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#ffcd44&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBorderBrushPointerOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#ffcd44&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBackgroundPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#10ca5100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonForegroundPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#ca5100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonBorderBrushPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#ca5100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在我们只是设置一些颜色值即修改了按钮在多种状态下的外观。而且在按下的过程中，还保留了按钮按下时的倾斜效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-26-lightweight-styling.gif&quot; alt=&quot;按钮更多的颜色定制&quot; /&gt;&lt;br /&gt;
▲ 按钮更多的颜色定制&lt;/p&gt;

&lt;p&gt;相比于 Template -&amp;gt; Edit Copy 这种重量级的样式与模板定义，在保证足够的样式定义的情况下，代码量是不是少了非常多了呢？&lt;/p&gt;

&lt;h2 id=&quot;如何找到控件支持的主题资源&quot;&gt;如何找到控件支持的主题资源&lt;/h2&gt;

&lt;p&gt;前面我们知道了如何定制轻量级样式，但实际做 UI 的时候，我怎么知道有哪些样式主题资源的值可以使用呢？&lt;/p&gt;

&lt;p&gt;一种方法是直接看微软官方文档，比如这里 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-theme-resources&quot;&gt;XAML theme resources&lt;/a&gt;；你可以在这篇文章中找到很多通用的主题资源的 Key 用来重写。不过实际上由于 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/communitytoolkit/?wt.mc_id=MVP&quot;&gt;Windows Community Toolkit&lt;/a&gt; 以及各种第三方控件库的存在，所以没有什么文档是可以把这些 Key 写全的；所以更重要的方法是我们能自己找到有哪些 Key 可以使用。&lt;/p&gt;

&lt;p&gt;找到 Key 的方法和定义一个全新的 Style / Template 一样，都可以通过 Visual Studio 的设计器视图（或者 Blend）实现。&lt;/p&gt;

&lt;h3 id=&quot;第一步前往-visual-studio-设计器视图&quot;&gt;第一步：前往 Visual Studio 设计器视图&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-26-09-03-50.png&quot; alt=&quot;Visual Studio 设计器视图&quot; /&gt;&lt;br /&gt;
▲ Visual Studio 设计器视图&lt;/p&gt;

&lt;h3 id=&quot;第二步在其中一个你想定制样式的控件上-右键---编辑模板---编辑副本&quot;&gt;第二步：在其中一个你想定制样式的控件上 右键 -&amp;gt; 编辑模板 -&amp;gt; 编辑副本&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-26-09-04-53.png&quot; alt=&quot;编辑模板&quot; /&gt;&lt;br /&gt;
▲ 编辑模板&lt;/p&gt;

&lt;p&gt;特别注意，如果你发现你的 “编辑副本” 是灰色的，说明你已经定制过样式了。将你已经定制的样式删除后，就可以再编辑副本了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-26-09-05-18.png&quot; alt=&quot;灰色的 “编辑副本”&quot; /&gt;&lt;br /&gt;
▲ 灰色的 “编辑副本”&lt;/p&gt;

&lt;h3 id=&quot;第三步寻找你感兴趣的主题资源的-key记下来准备定义&quot;&gt;第三步：寻找你感兴趣的主题资源的 Key，记下来准备定义&lt;/h3&gt;

&lt;p&gt;在编辑副本后，你可以在副本的代码中找到按钮的原生样式定义。比如一个按钮的样式是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ButtonStyle1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBackground}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonForeground}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderBrush&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBorderBrush}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderThickness&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBorderThemeThickness}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Padding&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;8,4,8,4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HorizontalAlignment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VerticalAlignment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FontFamily&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ContentControlThemeFontFamily}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FontWeight&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FontSize&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ControlContentThemeFontSize}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UseSystemFocusVisuals&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource UseSystemFocusVisuals}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FocusVisualMargin&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Template&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommonStates&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PointerUpThemeAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOver&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBackgroundPointerOver}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderBrush&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBorderBrushPointerOver}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonForegroundPointerOver}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PointerUpThemeAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBackgroundPressed}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderBrush&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBorderBrushPressed}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonForegroundPressed}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PointerDownThemeAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Disabled&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBackgroundDisabled}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderBrush&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonBorderBrushDisabled}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ObjectAnimationUsingKeyFrames&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DiscreteObjectKeyFrame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ButtonForegroundDisabled}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ObjectAnimationUsingKeyFrames&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateGroup&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContentPresenter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AutomationProperties.AccessibilityView=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Raw&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding BorderThickness}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderBrush=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding BorderBrush}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ContentTemplate=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding ContentTemplate}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Content}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ContentTransitions=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding ContentTransitions}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalContentAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding HorizontalContentAlignment}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Padding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Padding}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalContentAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding VerticalContentAlignment}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从中我们可以找到这些可以定义的主题资源 Key：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ButtonBackground&lt;/li&gt;
  &lt;li&gt;ButtonForeground&lt;/li&gt;
  &lt;li&gt;ButtonBorderBrush&lt;/li&gt;
  &lt;li&gt;ButtonBorderThemeThickness&lt;/li&gt;
  &lt;li&gt;ContentControlThemeFontFamily&lt;/li&gt;
  &lt;li&gt;ControlContentThemeFontSize&lt;/li&gt;
  &lt;li&gt;ButtonBackgroundPointerOver&lt;/li&gt;
  &lt;li&gt;ButtonBorderBrushPointerOver&lt;/li&gt;
  &lt;li&gt;ButtonForegroundPointerOver&lt;/li&gt;
  &lt;li&gt;ButtonBackgroundPressed&lt;/li&gt;
  &lt;li&gt;ButtonBorderBrushPressed&lt;/li&gt;
  &lt;li&gt;ButtonForegroundPressed&lt;/li&gt;
  &lt;li&gt;ButtonBackgroundDisabled&lt;/li&gt;
  &lt;li&gt;ButtonBorderBrushDisabled&lt;/li&gt;
  &lt;li&gt;ButtonForegroundDisabled&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;第四步轻量级样式定义&quot;&gt;第四步：轻量级样式定义&lt;/h3&gt;

&lt;p&gt;请先删除这份副本样式，这样你就可以进行 “轻量级样式定义” 了。代码量相比于上面这份完整样式可以少非常多。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/uwp-lightweight-xaml-styling.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/uwp-lightweight-xaml-styling.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>UWP 中的各种文件路径（用户、缓存、漫游、安装……）</title>
        <description>&lt;p&gt;UWP 提供了多种不同文件路径访问方式，对应到不同的文件路径中。可能我们只是简单用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationData.Current&lt;/code&gt; 获取一下可以读写的路径便能应付我们应用日常所需的各种文件读写需求，不过，UWP 还提供了更多的路径选项。&lt;/p&gt;

&lt;p&gt;本文将和你一起总结 UWP 中的各种各样的路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;uwp-中的路径种类&quot;&gt;UWP 中的路径种类&lt;/h2&gt;

&lt;p&gt;UWP 中可访问的路径有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.storage.applicationdata?wt.mc_id=MVP&quot;&gt;ApplicationData&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;用于储存应用的各种数据&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.package.installedlocation#Windows_ApplicationModel_Package_InstalledLocation?wt.mc_id=MVP&quot;&gt;Package.InstalledLocation&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;提供对应用程序包中各种文件的访问&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.storage.appdatapaths?wt.mc_id=MVP&quot;&gt;特殊文件夹&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;提供用户文档、用户收藏夹等特殊文件夹的访问&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;applicationdata&quot;&gt;ApplicationData&lt;/h2&gt;

&lt;p&gt;ApplicationData 提供应用程序自己创建的数据的读写能力。它包含这些文件夹：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Local: 储存在设备上，可被云端备份，在更新之后此数据保留&lt;/li&gt;
  &lt;li&gt;LocalCache: 储存在当前设备上，不备份，在更新后此数据保留&lt;/li&gt;
  &lt;li&gt;SharedLocal: 储存在设备上，为所有用户共享&lt;/li&gt;
  &lt;li&gt;Roaming: 对于同一个用户，会存在于安装了此应用的所用设备中&lt;/li&gt;
  &lt;li&gt;Temporary: 允许操作系统在任何时刻删除的临时文件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在智能感知提示的帮助下，你也可以找到对应的这几个文件夹：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-23-14-55-08.png&quot; alt=&quot;ApplicationData 的智能感知提示&quot; /&gt;&lt;br /&gt;
▲ ApplicationData 的智能感知提示&lt;/p&gt;

&lt;p&gt;这些不同的文件夹有着不同建议的用途。Local 文件夹，用来储存用户产生的数据（例如用户创建的文档等）；这部分数据在进行备份的时候会被备份下来。相比之下，LocalCache 和 Temporary 是不受备份影响的。&lt;/p&gt;

&lt;p&gt;额外的，&lt;/p&gt;

&lt;h2 id=&quot;packageinstalledlocation&quot;&gt;Package.InstalledLocation&lt;/h2&gt;

&lt;p&gt;应用程序可以访问安装后程序包所在的路径，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Package.Current.InstalledLocation&lt;/code&gt; 即可获取到应用程序包所在路径。&lt;/p&gt;

&lt;p&gt;当然，这部分的路径有更多的快捷访问方式，比如 Uri 以 &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; 开头，就是访问程序包所在路径：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foundation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/samples/logo.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还可以以 &lt;code class=&quot;highlighter-rouge&quot;&gt;ms-appx:///&lt;/code&gt; 协议开头：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foundation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ms-appx:///samples/logo.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StorageFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFileFromApplicationUriAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;特殊文件夹&quot;&gt;特殊文件夹&lt;/h2&gt;

&lt;p&gt;特殊文件夹可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;KnownFolders&lt;/code&gt; 类型获取，可以获取到照片、图片、音乐、视频等文件夹。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.storage.knownfolders?wt.mc_id=MVP&quot;&gt;KnownFolders Class (Windows.Storage) - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/all-kinds-of-paths-in-uwp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/all-kinds-of-paths-in-uwp.html</guid>
        
        
        <category>dotnet</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>C# 空合并操作符（??）不可重载？其实有黑科技可以间接重载！</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;??&lt;/code&gt; 操作符叫做 null-coalescing operator，即 null 合并运算符。如果此运算符的左操作数不为 null，则此运算符将返回左操作数；否则返回右操作数。&lt;/p&gt;

&lt;p&gt;在微软的官方 C# 文档中，此操作符被定义为不可重载。不过我们有方法可以间接实现这样的重载。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;运算符重载&quot;&gt;运算符重载&lt;/h2&gt;

&lt;p&gt;你可以阅读 &lt;a href=&quot;/post/overridable-operators-in-csharp&quot;&gt;C# 中那些可以被重载的操作符，以及使用它们的那些丧心病狂的语法糖&lt;/a&gt; 了解 C# 中提供的所有可以重载的操作符。在此文中，&lt;code class=&quot;highlighter-rouge&quot;&gt;??&lt;/code&gt; 被明确定义为不可重载。&lt;/p&gt;

&lt;p&gt;你更可以在微软官方文档中找到这样的说法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators?wt.mc_id=MVP&quot;&gt;Overloadable operators (C# Programming Guide)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators?wt.mc_id=MVP&quot;&gt;可重载运算符（C# 编程指南）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;=, ., ?:, ??, -&amp;gt;, =&amp;gt;, f(x), as, checked, unchecked, default, delegate, is, new, sizeof, typeof&lt;br /&gt;
These operators cannot be overloaded.&lt;br /&gt;
这些运算符无法进行重载。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;编写-nullablestring-的--重载&quot;&gt;编写 NullableString 的 ?? 重载&lt;/h2&gt;

&lt;p&gt;我们先写一个空壳子。连构造函数都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;private&lt;/code&gt; 的，这个类当然几乎不可用啦。&lt;/p&gt;

&lt;p&gt;特别注意，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.NullableString&lt;/code&gt; 用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt; 类型，这样能与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 的用法上接近。也就是说，我们可以确保其值实际上永不为 null。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NullableString&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在我们挑战一下官方说好了不能重载的 &lt;code class=&quot;highlighter-rouge&quot;&gt;??&lt;/code&gt; 重载（作死）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-15-10-45-58.png&quot; alt=&quot;试着重载 ??&quot; /&gt;&lt;br /&gt;
▲ 试着重载 ??&lt;/p&gt;

&lt;p&gt;很明显，既不是可重载的一员运算符也不是可重载的二元运算符。&lt;/p&gt;

&lt;p&gt;现在我们试试隐式转换：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而这样的写法实际上并无实际用途。&lt;/p&gt;

&lt;p&gt;但是，我们可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableString&lt;/code&gt; 后面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，C# 竟然允许隐式转换的时候，参数和返回值都不是此类型。当然，实际上这只对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; 生效，如果你试图写别的类型，是不可以的。&lt;/p&gt;

&lt;p&gt;为了方便，我们重写一下 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt;，部分场景下可以代替隐式转换，少写一些代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullableString&lt;/code&gt; 类型的完整代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NullableString&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注释就你自己添加吧。&lt;/p&gt;

&lt;h2 id=&quot;一些注意事项&quot;&gt;一些注意事项&lt;/h2&gt;

&lt;p&gt;这里有一些好玩的事情需要分享。比如我们写出如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你觉得 &lt;code class=&quot;highlighter-rouge&quot;&gt;value0&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;value1&lt;/code&gt; 分别会得到什么呢？&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;呃……&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;value0&lt;/code&gt; 得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;value1&lt;/code&gt; 得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;另外，如果你将一开始的初始值设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，那又可以得到什么结果呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一样的，&lt;code class=&quot;highlighter-rouge&quot;&gt;value0&lt;/code&gt; 得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;value1&lt;/code&gt; 得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;另外，你可以从 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 强转出你需要的类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullableString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/overload-null-coalescing-operator-in-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/overload-null-coalescing-operator-in-csharp.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET 中各种混淆（Obfuscation）的含义、原理、实际效果和不同级别的差异（使用 SmartAssembly）</title>
        <description>&lt;p&gt;长文预警！！！&lt;/p&gt;

&lt;p&gt;UWP 程序有 .NET Native 可以将程序集编译为本机代码，逆向的难度会大很多；而基于 .NET Framework 和 .NET Core 的程序却没有 .NET Native 的支持。虽然有 Ngen.exe 可以编译为本机代码，但那只是在用户计算机上编译完后放入了缓存中，而不是在开发者端编译。&lt;/p&gt;

&lt;p&gt;于是有很多款混淆工具来帮助混淆基于 .NET 的程序集，使其稍微难以逆向。本文介绍 Smart Assembly 各项混淆参数的作用以及其实际对程序集的影响。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文不会讲 SmartAssembly 的用法，因为你只需打开它就能明白其基本的使用。&lt;/p&gt;

&lt;p&gt;感兴趣可以先下载：&lt;a href=&quot;https://www.red-gate.com/products/dotnet-development/smartassembly/index&quot;&gt;.NET Obfuscator, Error Reporting, DLL Merging - SmartAssembly&lt;/a&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备&quot;&gt;准备&lt;/h2&gt;

&lt;p&gt;我们先需要准备程序集来进行混淆试验。这里，我使用 &lt;a href=&quot;ms-windows-store://pdp/?productid=9P8LNZRNJX85&quot;&gt;Whitman&lt;/a&gt; 来试验。它在 &lt;a href=&quot;https://github.com/walterlv/Whitman&quot;&gt;GitHub 上开源&lt;/a&gt;，并且有两个程序集可以试验它们之间的相互影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-15-14-44.png&quot; alt=&quot;准备程序集&quot; /&gt;&lt;/p&gt;

&lt;p&gt;额外想吐槽一下，SmartAssembly 的公司 Red Gate 一定不喜欢这款软件，因为界面做成下面这样竟然还长期不更新：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-16-24-01.png&quot; alt=&quot;无力吐槽的界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而且，如果要成功编译，还得用上同为 Red Gate 家出品的 SQL Server，如果不装，软件到处弹窗报错。只是报告错误而已，干嘛还要开发者装一个那么重量级的 SQL Server 啊！详见：&lt;a href=&quot;https://forum.red-gate.com/discussion/83290/why-is-sql-server-required&quot;&gt;Why is SQL Server required — Redgate forums&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;smartassembly&quot;&gt;SmartAssembly&lt;/h2&gt;

&lt;p&gt;SmartAssembly 本质上是保护应用程序不被逆向或恶意篡改。目前我使用的版本是 6，它提供了对 .NET Framework 程序的多种保护方式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;强签名 Strong Name Signing&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;强签名可以确保程序之间的依赖关系是严格确定的，如果对其中的一个依赖进行篡改，将导致无法加载正确的程序集。&lt;/li&gt;
      &lt;li&gt;微软提供了强签名工具，所以可以无需使用 SmartAssembly 的：
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool?wt.mc_id=MVP&quot;&gt;Sn.exe (Strong Name Tool) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-sign-an-assembly-with-a-strong-name?wt.mc_id=MVP&quot;&gt;How to: Sign an Assembly with a Strong Name - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;自动错误上报 Automated Error Reporting&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会自动向 exe 程序注入异常捕获与上报的逻辑。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;功能使用率上报 Feature Usage Reporting&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会修改每个方法，记录这些方法的调用次数并上报。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;依赖合并 Dependencies Merging&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将程序集中你勾选的的依赖与此程序集合并成一个整的程序集。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;依赖嵌入 Dependencies Embedding&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将依赖以加密并压缩的方式嵌入到程序集中，运行时进行解压缩与解密。&lt;/li&gt;
      &lt;li&gt;其实这只是方便了部署（一个 exe 就能发给别人），并不能真正保护程序集，因为实际运行时还是解压并解密出来了。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;裁剪 Pruning&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将没有用到的字段、属性、方法、事件等删除。它声称删除了这些就能让程序逆向后代码更难读懂。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;名称混淆 Obfuscation&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;修改类型、字段、属性、方法等的名称。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;流程混淆 Control Flow Obfuscation&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;修改方法内的执行逻辑，使其执行错综复杂。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;动态代理 References Dynamic Proxy&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将方法的调用转到动态代理上。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;资源压缩加密 Resources Compression and Encryption&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将资源以加密并压缩的方式嵌入到程序集中，运行时进行解压缩与解密。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;字符串压缩加密 Strings Encoding&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;SmartAssembly 会将字符串都进行加密，运行时自动对其进行解密。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;防止 MSIL Disassembler 对其进行反编译 MSIL Disassembler Protection&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;在程序集中加一个 Attribute，这样 MSIL Disassembler 就不会反编译这个程序集。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;密封类&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;如果 SmartAssembly 发现一个类可以被密封，就会把它密封，这样能获得一点点性能提升。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;生成调试信息 Generate Debugging Information&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;可以生成混淆后的 pdb 文件&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上所有 SmartAssembly 对程序集的修改中，我标为 &lt;strong&gt;粗体&lt;/strong&gt; 的是真的在做混淆，而标为 &lt;em&gt;斜体&lt;/em&gt; 的是一些辅助功能。&lt;/p&gt;

&lt;p&gt;后面我只会说明其混淆功能。&lt;/p&gt;

&lt;h2 id=&quot;裁剪-pruning&quot;&gt;裁剪 Pruning&lt;/h2&gt;

&lt;p&gt;我故意在 Whitman.Core 中写了一个没有被用到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 类 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnusedClass&lt;/code&gt;，如果我们开启了裁剪，那么这个类将消失。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-17-10-51.png&quot; alt=&quot;消失的类&quot; /&gt;&lt;br /&gt;
▲ 没用到的类将消失&lt;/p&gt;

&lt;p&gt;特别注意，如果标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;InternalsVisibleTo&lt;/code&gt;，尤其注意不要不小心被误删了。&lt;/p&gt;

&lt;h2 id=&quot;名称混淆-obfuscation&quot;&gt;名称混淆 Obfuscation&lt;/h2&gt;

&lt;h3 id=&quot;类方法名与字段名的混淆&quot;&gt;类/方法名与字段名的混淆&lt;/h3&gt;

&lt;p&gt;名称混淆中，类名和方法名的混淆有三个不同级别：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;等级 1 是使用 ASCII 字符集&lt;/li&gt;
  &lt;li&gt;等级 2 是使用不可见的 Unicode 字符集&lt;/li&gt;
  &lt;li&gt;等级 3 是使用高级重命名算法的不可见的 Unicode 字符集&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要注意：对于部分程序集，&lt;strong&gt;类与方法名（NameMangling）的等级只能选为 3，否则混淆程序会无法完成编译&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;字段名的混淆有三个不同级别：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;等级 1 是源码中字段名称和混淆后字段名称一一对应&lt;/li&gt;
  &lt;li&gt;等级 2 是在一个类中的不同字段使用不同名称即可（这不废话吗，不过 SmartAssembly 应该是为了强调与等级 1 和等级 3 的不同，必须写一个描述）&lt;/li&gt;
  &lt;li&gt;等级 3 是允许不同类中的字段使用相同的名字（这样能够更加让人难以理解）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要注意：对于部分程序集，&lt;strong&gt;字段名（FieldsNameMangling）的等级只能选为 2 或 3，否则混淆程序会无法完成编译&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;实际试验中，以上各种组合经常会出现无法编译的情况。&lt;/p&gt;

&lt;p&gt;下面是 Whitman 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;RandomIdentifier&lt;/code&gt; 类中的部分字段在混淆后的效果：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x04000001 RID: 1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilerGenerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggerBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Token: 0x04000002 RID: 2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Random&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Token: 0x04000003 RID: 3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这部分的原始代码可以在 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名）&lt;/a&gt; 找到。&lt;/p&gt;

&lt;p&gt;如果你需要在混淆时使用名称混淆，你只需要在以上两者的组合中找到一个能够编译通过的组合即可，不需要特别在意等级 1~3 的区别，因为实际上都做了混淆，1~3 的差异对逆向来说难度差异非常小的。&lt;/p&gt;

&lt;p&gt;需要 &lt;strong&gt;特别小心如果有 &lt;code class=&quot;highlighter-rouge&quot;&gt;InternalsVisibleTo&lt;/code&gt; 或者依据名称的反射调用，这种混淆下极有可能挂掉&lt;/strong&gt;！！！&lt;strong&gt;请充分测试你的软件，切记&lt;/strong&gt;！！！&lt;/p&gt;

&lt;h3 id=&quot;转移方法-changemethodparent&quot;&gt;转移方法 ChangeMethodParent&lt;/h3&gt;

&lt;p&gt;如果开启了 ChangeMethodParent，那么混淆可能会将一个类中的方法转移到另一个类中，这使得逆向时对类型含义的解读更加匪夷所思。&lt;/p&gt;

&lt;h3 id=&quot;排除特定的命名空间&quot;&gt;排除特定的命名空间&lt;/h3&gt;

&lt;p&gt;如果你的程序集中确实存在需要被按照名称反射调用的类型，或者有 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 的类/方法需要被友元程序集调用，请排除这些命名空间。&lt;/p&gt;

&lt;h2 id=&quot;流程混淆-control-flow-obfuscation&quot;&gt;流程混淆 Control Flow Obfuscation&lt;/h2&gt;

&lt;p&gt;列举我在 Whitman.Core 中的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyllableMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vowel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToTitleCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vowel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 这个方法可以在 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名）&lt;/a&gt; 找到。&lt;/p&gt;

&lt;p&gt;流程混淆修改方法内部的实现。为了了解各种不同的流程混淆级别对代码的影响，我为每一个混淆级别都进行反编译查看。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-17-19-24.png&quot; alt=&quot;没有混淆&quot; /&gt;&lt;br /&gt;
▲ 没有混淆&lt;/p&gt;

&lt;h3 id=&quot;0-级流程混淆&quot;&gt;0 级流程混淆&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-17-26-39.png&quot; alt=&quot;0 级流程混淆&quot; /&gt;&lt;br /&gt;
▲ 0 级流程混淆&lt;/p&gt;

&lt;h3 id=&quot;1-级流程混淆&quot;&gt;1 级流程混淆&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-17-24-43.png&quot; alt=&quot;1 级流程混淆&quot; /&gt;&lt;br /&gt;
▲ 1 级流程混淆&lt;/p&gt;

&lt;p&gt;可以发现 0 和 1 其实完全一样。又被 SmartAssembly 耍了。&lt;/p&gt;

&lt;h3 id=&quot;2-级流程混淆&quot;&gt;2 级流程混淆&lt;/h3&gt;

&lt;p&gt;2 级流程混淆代码很长，所以我没有贴图：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000004 RID: 4 RVA: 0x00002070 File Offset: 0x00000270&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_10E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_134&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_8E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_6C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyllableMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num13&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_8E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_11E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_10E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_11E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num13&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_134&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num13&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToTitleCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_10E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_134&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_6C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 2 级流程混淆&lt;/p&gt;

&lt;p&gt;这时就发现代码的可读性降低了，需要耐心才能解读其含义。&lt;/p&gt;

&lt;h3 id=&quot;3-级流程混淆&quot;&gt;3 级流程混淆&lt;/h3&gt;

&lt;p&gt;以下是 3 级流程混淆：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000004 RID: 4 RVA: 0x0000207C File Offset: 0x0000027C&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_84&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_12A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_73&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyllableMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_81&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_84&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_114&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_114&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_12A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToTitleCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_81&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_12A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IL_73&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 3 级流程混淆&lt;/p&gt;

&lt;p&gt;3 级流程混淆并没有比 2 级高多少，可读性差不多。不过需要注意的是，这些差异并不是随机差异，因为重复生成得到的流程结果是相同的。&lt;/p&gt;

&lt;h3 id=&quot;4-级流程混淆&quot;&gt;4 级流程混淆&lt;/h3&gt;

&lt;p&gt;以下是 4 级流程混淆：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000004 RID: 4 RVA: 0x0000207C File Offset: 0x0000027C&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;stackalloc&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(;;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;sbyte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyllableMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(;;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;sbyte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToTitleCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 4 级流程混淆&lt;/p&gt;

&lt;p&gt;我们发现，4 级已经开始使用没有含义的指针来转换我们的内部实现了。这时除了外部调用以外，代码基本已无法解读其含义了。&lt;/p&gt;

&lt;h2 id=&quot;动态代理-references-dynamic-proxy&quot;&gt;动态代理 References Dynamic Proxy&lt;/h2&gt;

&lt;p&gt;还是以上一节中我们 Generate 方法作为示例，在开启了动态代理之后（仅开启动态代理，其他都关掉），方法变成了下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000004 RID: 4 RVA: 0x0000206C File Offset: 0x0000026C&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyllableMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0004&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0004&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 动态代理&lt;/p&gt;

&lt;p&gt;注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;_random.Next(0, 9)&lt;/code&gt; 变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;\u0001.~\u0001(this._random, 0, 9)&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Math.Sqrt(num)&lt;/code&gt; 变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;\u0002.\u0002(num)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;也就是说，一些常规方法的调用被替换成了一个代理类的调用。那么代理类在哪里呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-18-07-53.png&quot; alt=&quot;生成的代理类&quot; /&gt;&lt;br /&gt;
▲ 生成的代理类&lt;/p&gt;

&lt;p&gt;生成的代理类都在根命名空间下。比如刚刚的 &lt;code class=&quot;highlighter-rouge&quot;&gt;\u0001.~\u0001&lt;/code&gt; 调用，就是下面这个代理类：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x0200001A RID: 26&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;u0001&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MulticastDelegate&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000030 RID: 48&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000031 RID: 49&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Token: 0x06000032 RID: 50 RVA: 0x000030A8 File Offset: 0x000012A8&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;MemberRefsProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateMemberRefsDelegates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Token: 0x04000016 RID: 22&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Token: 0x04000017 RID: 23&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;字符串编码与加密-strings-encoding&quot;&gt;字符串编码与加密 Strings Encoding&lt;/h2&gt;

&lt;h3 id=&quot;字符串统一收集编码-encode&quot;&gt;字符串统一收集编码 Encode&lt;/h3&gt;

&lt;p&gt;字符串编码将程序集中的字符串都统一收集起来，存为一个资源；然后提供一个辅助类统一获取这些字符串。&lt;/p&gt;

&lt;p&gt;比如 Whitman.Core 中的字符串现在被统一收集了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-18-16-06.png&quot; alt=&quot;统一收集的字符串和解密辅助类&quot; /&gt;&lt;br /&gt;
▲ 统一收集的字符串和解密辅助类&lt;/p&gt;

&lt;p&gt;在我的项目中，统一收集的字符串可以形成下面这份字符串（也即是上图中 Resources 文件夹中的那个文件内容）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cQ==dw==cg==dA==eQ==cA==cw==ZA==Zg==Zw==aA==ag==aw==bA==eg==eA==
Yw==dg==Yg==bg==bQ==dHI=ZHI=Y2g=d2g=c3Q=YQ==ZQ==aQ==bw==dQ==YXI=
YXM=YWk=YWlyYXk=YWw=YWxsYXc=ZWU=ZWE=ZWFyZW0=ZXI=ZWw=ZXJlaXM=aXI=
b3U=b3I=b28=b3c=dXI=MjAxOC0wOC0yNlQxODoxMDo0Mw==`VGhpcyBhc3NlbWJseSBoYXMgY
mVlbiBidWlsdCB3aXRoIFNtYXJ0QXNzZW1ibHkgezB9LCB3aGljaCBoYXMgZXhwaXJlZC4=RXZhbHVh
dGlvbiBWZXJzaW9uxVGhpcyBhc3NlbWJseSBoYXMgYmVlbiBidWlsdCB3aXRoIFNtYXJ0QXNzZW1ibHk
gezB9LCBhbmQgdGhlcmVmb3JlIGNhbm5vdCBiZSBkaXN0cmlidXRlZC4=IA==Ni4xMi41Ljc5OQ==
U21hcnRBc3NlbWJseQ==UGF0aA==U29mdHdhcmVcUmVkIEdhdGVc(U29mdHdhcmVcV293NjQzMk5vZ
GVcUmVkIEdhdGVc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;虽然字符串难以读懂，但其实我原本就是这么写的；给你看看我的原始代码就知道了（来自 &lt;a href=&quot;/post/algorithm-of-generating-random-identifiers&quot;&gt;冷算法：自动生成代码标识符（类名、方法名、变量名）&lt;/a&gt;）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;q&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;tr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;st&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;生成的字符串获取辅助类就像下面这样不太容易读懂：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x0200000A RID: 10&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Strings&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0600001C RID: 28 RVA: 0x00002B94 File Offset: 0x00000D94&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stringID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheStrings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashtableLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashtable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stringID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++];&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;63&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromBase64String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Intern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheStrings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashtableLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashtable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0600001D RID: 29 RVA: 0x00002CF4 File Offset: 0x00000EF4&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MustUseCache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheStrings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashtable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OffsetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifestResourceStream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetManifestResourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{f6b5a51a-b2fb-4143-af01-e2295062799f}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifestResourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;manifestResourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;manifestResourceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0400000C RID: 12&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MustUseCache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0400000D RID: 13&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OffsetValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;203&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0400000E RID: 14&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x0400000F RID: 15&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hashtable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x04000010 RID: 16&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hashtableLock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x04000011 RID: 17&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheStrings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x04000012 RID: 18&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;生成字符串获取辅助类后，原本写着字符串的地方就会被替换为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get(int)&lt;/code&gt; 方法的调用。&lt;/p&gt;

&lt;h3 id=&quot;字符串压缩加密-compress&quot;&gt;字符串压缩加密 Compress&lt;/h3&gt;

&lt;p&gt;前面那份统一收集的字符串依然还是明文存储为资源，但还可以进行压缩。这时，Resources 中的那份字符串资源现在是二进制文件（截取前 256 字节）：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00000000:	7b7a	7d02	efbf	bdef	bfbd	4def	bfbd	efbf
00000010:	bd7e	6416	efbf	bd6a	efbf	bd22	efbf	bd08
00000020:	efbf	bdef	bfbd	4c42	7138	72ef	bfbd	efbf
00000030:	bd54	1337	efbf	bd0e	22ef	bfbd	69ef	bfbd
00000040:	613d	efbf	bd6e	efbf	bd35	efbf	bd0a	efbf
00000050:	bd33	6043	efbf	bd26	59ef	bfbd	5471	efbf
00000060:	bdef	bfbd	2cef	bfbd	18ef	bfbd	6def	bfbd
00000070:	efbf	bdef	bfbd	64ef	bfbd	c9af	efbf	bdef
00000080:	bfbd	efbf	bd4b	efbf	bdef	bfbd	66ef	bfbd
00000090:	1e70	efbf	bdef	bfbd	ce91	71ef	bfbd	1d5e
000000a0:	1863	efbf	bd16	0473	25ef	bfbd	2204	efbf
000000b0:	bdef	bfbd	11ef	bfbd	4fef	bfbd	265a	375f
000000c0:	7bef	bfbd	19ef	bfbd	d5bd	efbf	bdef	bfbd
000000d0:	efbf	bd70	71ef	bfbd	efbf	bd05	c789	efbf
000000e0:	bd51	eaae	beef	bfbd	ee97	adef	bfbd	0a33
000000f0:	d986	141c	2bef	bfbd	efbf	bdef	bfbd	1fef
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这份压缩的字符串在程序启动的时候会进行一次解压，随后就直接读取解压后的字符串了。所以会占用启动时间（虽然不长），但不会占用太多运行时时间。&lt;/p&gt;

&lt;p&gt;为了能够解压出这些压缩的字符串，&lt;code class=&quot;highlighter-rouge&quot;&gt;Strings&lt;/code&gt; 类相比于之前会在读取后进行一次解压缩（解密）。可以看下面我额外标注出的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings&lt;/code&gt; 类新增的一行。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   using (Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(&quot;{4f639d09-ce0f-4092-b0c7-b56c205d48fd}&quot;))
   {
       int num = Convert.ToInt32(manifestResourceStream.Length);
       byte[] buffer = new byte[num];
       manifestResourceStream.Read(buffer, 0, num);
&lt;span class=&quot;gi&quot;&gt;++     Strings.bytes = SimpleZip.Unzip(buffer);
&lt;/span&gt;       manifestResourceStream.Close();
   }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于嵌入其中的解压与解密类 &lt;code class=&quot;highlighter-rouge&quot;&gt;SimpleZip&lt;/code&gt;，我就不能贴出来了，因为反编译出来有 3000+ 行：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-19-18-34-18.png&quot; alt=&quot;3000+ 行的解压与解密类&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;字符串缓存-usecache&quot;&gt;字符串缓存 UseCache&lt;/h3&gt;

&lt;p&gt;与其他的缓存策略一样，每次获取字符串都太消耗计算资源的话，就可以拿内存空间进行缓存。&lt;/p&gt;

&lt;p&gt;在实际混淆中，我发现无论我是否开启了字符串缓存，实际 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get&lt;/code&gt; 方法都会缓存字符串。你可以回到上面去重新阅读 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get&lt;/code&gt; 方法的代码，发现其本来就已带缓存。这可能是 SmartAssembly 的 Bug。&lt;/p&gt;

&lt;h3 id=&quot;使用类的内部委托获取字符串-useimprovedencoding&quot;&gt;使用类的内部委托获取字符串 UseImprovedEncoding&lt;/h3&gt;

&lt;p&gt;之前的混淆都会在原来有字符串地方使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get&lt;/code&gt; 来获取字符串。而如果开启了这一选项，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get&lt;/code&gt; 就不是全局调用的了，而是在类的内部调用一个委托字段。&lt;/p&gt;

&lt;p&gt;比如从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Strings.Get&lt;/code&gt; 调用修改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;\u0010(),&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;\u0010&lt;/code&gt; 是我们自己的类 &lt;code class=&quot;highlighter-rouge&quot;&gt;RandomIdentifier&lt;/code&gt; 内部的被额外加进去的一个字段 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal static GetString \u0010;&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;防止-msil-disassembler-对其进行反编译-msil-disassembler-protection&quot;&gt;防止 MSIL Disassembler 对其进行反编译 MSIL Disassembler Protection&lt;/h2&gt;

&lt;p&gt;这其实是个没啥用的选项，因为我们程序集只会多出一个全局的特性：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SuppressIldasm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只有 MSIL Disassembler 和基于 MSIL Disassembler 的工具认这个特性。真正想逆向程序集的，根本不会在乎 MSIL Disassembler 被禁掉。&lt;/p&gt;

&lt;p&gt;dnSpy 和 dotPeek 实际上都忽略了这个特性，依然能毫无障碍地反编译。&lt;/p&gt;

&lt;p&gt;dnSpy 可以做挺多事儿的，比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95-Windows-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;断点调试 Windows 源代码 - lindexi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/edit-and-recompile-assembly-using-dnspy&quot;&gt;神器如 dnSpy，无需源码也能修改 .NET 程序 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;密封&quot;&gt;密封&lt;/h2&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OtherOptimizations&lt;/code&gt; 选项中，有一项 &lt;code class=&quot;highlighter-rouge&quot;&gt;SealClasses&lt;/code&gt; 可以将所有可以密封的类进行密封（当然，此操作不会修改 API）。&lt;/p&gt;

&lt;p&gt;在上面的例子中，由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;RandomIdentifier&lt;/code&gt; 是公有类，可能被继承，所以只有预先写的内部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnusedClass&lt;/code&gt; 被其标记为密封了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Token: 0x02000003 RID: 3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnusedClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Token: 0x06000007 RID: 7 RVA: 0x000026D0 File Offset: 0x000008D0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Token: 0x06000008 RID: 8 RVA: 0x000026D4 File Offset: 0x000008D4&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;实际项目中我该如何选择&quot;&gt;实际项目中，我该如何选择&lt;/h2&gt;

&lt;p&gt;既然你希望选择“混淆”，那么你肯定是希望能进行最大程度的保护。在保证你没有额外产生 Bug，性能没有明显损失的情况下，能混淆得多厉害就混淆得多厉害。&lt;/p&gt;

&lt;p&gt;基于这一原则，我推荐的混淆方案有（按推荐顺序排序）：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;流程混淆
    &lt;ul&gt;
      &lt;li&gt;建议必选&lt;/li&gt;
      &lt;li&gt;直接选用 4 级流程（不安全代码）混淆，如果出问题才换为 3 级（goto）混淆，理论上不需要使用更低级别&lt;/li&gt;
      &lt;li&gt;流程混淆对性能的影响是非常小的，因为多执行的代码都是有编译期级别优化的，没有太多性能开销的代码&lt;/li&gt;
      &lt;li&gt;流程混淆仅影响实现，不修改 API，所以基本不会影响其他程序各种对此程序集的调用&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;名称混淆
    &lt;ul&gt;
      &lt;li&gt;尽量选择&lt;/li&gt;
      &lt;li&gt;任意选择类/方法名和字段名的级别，只要能编译通过就行（因为无论选哪个，对程序的影响都一样，逆向的难度差异也较小）&lt;/li&gt;
      &lt;li&gt;名称混淆不影响程序执行性能，所以只要能打开，就尽量打开&lt;/li&gt;
      &lt;li&gt;如果有 &lt;code class=&quot;highlighter-rouge&quot;&gt;InternalsVisibleTo&lt;/code&gt; 或者可能被其他程序集按名称反射调用，请：
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;关闭此混淆&lt;/strong&gt;&lt;/li&gt;
          &lt;li&gt;使用 Exclude 排除特定命名空间，使此命名空间下的类/方法名不进行名称混淆&lt;/li&gt;
          &lt;li&gt;如果你能接受用 Attribute 标记某些类不应该混淆类名，也可以使用这些标记（只是我不推荐这么做，这让混淆污染了自己的代码）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;动态代理
    &lt;ul&gt;
      &lt;li&gt;推荐选择&lt;/li&gt;
      &lt;li&gt;动态代理仅影响实现，不修改 API，所以基本不会影响其他程序各种对此程序集的调用&lt;/li&gt;
      &lt;li&gt;动态代理会生成新的类/委托来替换之前的方法调用，所以可能造成非常轻微的性能损失（一般可以忽略）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;字符串压缩加密
    &lt;ul&gt;
      &lt;li&gt;可以选择&lt;/li&gt;
      &lt;li&gt;由于所有的字符串都被统一成一个资源，如果额外进行压缩加密，那么逆向时理解程序的含义将变得非常困难（没有可以参考的锚点）&lt;/li&gt;
      &lt;li&gt;会对启动时间有轻微的性能影响，如果额外压缩加密，那么会有更多性能影响；如果你对启动性能要求较高，还是不要选了&lt;/li&gt;
      &lt;li&gt;会轻微增加内存占用和读取字符串时的 CPU 占用，如果你对程序性能要求非常高，还是不要选了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以上四种混淆方式从四个不同的维度对你类与方法的实现进行了混淆，使得你写的类的任何地方都变得无法辨认。流程混淆修改方法内实现的逻辑，名称混淆修改类/属性/方法的名称，动态代理将方法内对其他方法的调用变得不再直接，字符串压缩加密将使得字符串不再具有可读的含义。对逆向阅读影响最大的就是以上 4 种混淆了，如果可能，建议都选择开启。&lt;/p&gt;

&lt;p&gt;如果你的程序中有需要保护的“嵌入的资源”，在没有自己的保护手段的情况下，可以使用“资源压缩加密”。不过，我更加推荐你自己进行加密。&lt;/p&gt;

&lt;p&gt;至于 SmartAssembly 推荐的其他选项，都是噱头重于实际效果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;裁剪
    &lt;ul&gt;
      &lt;li&gt;一般也不会有多少开发者会故意往程序集中写一些不会用到的类吧！&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;依赖合并/依赖嵌入
    &lt;ul&gt;
      &lt;li&gt;并不会对逆向造成障碍，开不开启差别不大，反而降低了性能&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;防止 MSIL Disassembler 反编译
    &lt;ul&gt;
      &lt;li&gt;并不会对逆向造成障碍，防君子不防小人&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;密封类
    &lt;ul&gt;
      &lt;li&gt;声称可以提升性能，但这点性能提升微乎其微&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SmartAssembly 的官方文档写得还是太简单了，很难得到每一个设置项的含义和实际效果。&lt;/p&gt;

&lt;p&gt;以上这些信息的得出，离不开 &lt;a href=&quot;https://github.com/0xd4d/dnSpy&quot;&gt;dnSpy&lt;/a&gt; 的反编译。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://documentation.red-gate.com/sa6&quot;&gt;SmartAssembly 6 documentation - SmartAssembly 6 - Product Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://documentation.red-gate.com/sa6/obfuscating-your-code-with-smartassembly/obfuscating-code-with-name-mangling&quot;&gt;Obfuscating code with name mangling - SmartAssembly 6 - Product Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/obfuscation-configurations-of-smart-assembly.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/obfuscation-configurations-of-smart-assembly.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>让控制台支持 ANSI 转义序列，输出下划线、修改颜色或其他控制</title>
        <description>&lt;p&gt;各种操作系统的控制台都支持 ANSI 转义序列（ANSI Escape Code）。使用转义序列，可以对控制台进行很多额外的定制，例如修改颜色、修改标题栏，将文字添加下划线等。&lt;/p&gt;

&lt;p&gt;当然，.NET 已经帮助我们封装了很大的一部分功能了，我们重点可以放在 .NET 没有封装的那部分上。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;基本的准备代码&quot;&gt;基本的准备代码&lt;/h2&gt;

&lt;p&gt;在开始之前，我们先添加一些基础性代码，这是对系统核心功能的调用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;STD_OUTPUT_HANDLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENABLE_VIRTUAL_TERMINAL_PROCESSING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0004&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetStdHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nStdHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetConsoleMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hConsoleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetConsoleMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hConsoleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 Main 函数中，添加一些调用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetStdHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STD_OUTPUT_HANDLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;GetConsoleMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENABLE_VIRTUAL_TERMINAL_PROCESSING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SetConsoleMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 我们准备在这里添加新的代码。&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;开始使用-ansi-转义序列&quot;&gt;开始使用 ANSI 转义序列&lt;/h2&gt;

&lt;h3 id=&quot;添加下划线&quot;&gt;添加下划线&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UNDERLINE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\x1B[4m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RESET&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\x1B[0m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Some &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UNDERLINE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;underlined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-14-02-54.png&quot; alt=&quot;下划线转义&quot; /&gt;&lt;br /&gt;
▲ 下划线转义&lt;/p&gt;

&lt;h3 id=&quot;修改颜色&quot;&gt;修改颜色&lt;/h3&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RED&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\x1B[31m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Some &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UNDERLINE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;underlined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; and &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-14-09-53.png&quot; alt=&quot;颜色转义&quot; /&gt;&lt;br /&gt;
▲ 颜色转义（当然，.NET 封装有 API）&lt;/p&gt;

&lt;h3 id=&quot;其他转义序列&quot;&gt;其他转义序列&lt;/h3&gt;

&lt;p&gt;其他转义序列，可阅读 &lt;a href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code&quot;&gt;ANSI escape code - Wikipedia&lt;/a&gt;。不过 Windows 能支持的并不多。&lt;/p&gt;

&lt;p&gt;关于颜色，不同控制台上对于相同转义序列的颜色值和颜色支持程度也不同。&lt;/p&gt;

&lt;h2 id=&quot;关于-enable_virtual_terminal_processing&quot;&gt;关于 ENABLE_VIRTUAL_TERMINAL_PROCESSING&lt;/h2&gt;

&lt;p&gt;这是用来开启虚拟终端处理的一个标识，Windows 从一开始就默认关闭这个标识，必须通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetConsoleMode&lt;/code&gt; 手工开启。虽然在 10.0.10586 版本时短暂开启了一个版本，随后在 10.0.14393 中又再次默认关闭了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/console/setconsolemode?wt.mc_id=MVP&quot;&gt;SetConsoleMode function - Windows Console - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rprichard/winpty/issues/92&quot;&gt;Win10 New Console: Enable ENABLE_VIRTUAL_TERMINAL_PROCESSING by default (or with a flag) · Issue #92 · rprichard/winpty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wpdev.uservoice.com/forums/266908-command-prompt-console-windows-subsystem-for-l/suggestions/15617610--re-enable-enable-virtual-terminal-processing-by&quot;&gt;(Re?)enable ENABLE_VIRTUAL_TERMINAL_PROCESSING by default – Welcome to the Windows developer feedback site!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code&quot;&gt;ANSI escape code - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/5237666/6233938&quot;&gt;c# - adding text decorations to console output - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.top-password.com/blog/windows-10-command-prompt-new-console-vs-legacy-console/&quot;&gt;Windows 10 Command Prompt: New Console vs. Legacy Console - Password Recovery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/enable-virtual-terminal-processing.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/enable-virtual-terminal-processing.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>UWP 应用中 CoreApplication / Application, CoreWindow / Window 之间的区别</title>
        <description>&lt;p&gt;在 StackOverflow 上看到有小伙伴询问 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 它们的含义以及它们之间的区别。&lt;/p&gt;

&lt;p&gt;于是我整理了这篇文章。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/core-application-window-of-uwp.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/core-application-window-of-uwp-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;StackOverflow 上的地址：&lt;a href=&quot;https://stackoverflow.com/a/51585979/6233938&quot;&gt;c# - CoreApplicationView vs CoreWindow vs ApplicationView - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;命名空间&quot;&gt;命名空间&lt;/h2&gt;

&lt;p&gt;类的完整含义经常需要配合其命名空间查看，所以我们有必要将这几个类的完整名称拿出来看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplication&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplicationView&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.Application&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.ViewManagement.ApplicationView&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Core.CoreWindow&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.Window&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;额外的，如果你关心标题栏，还有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplicationViewTitleBar&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.ViewManagement.ApplicationViewTitleBar&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;再额外的，如果你关心线程模型，还有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Core.CoreDispatcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.DispatcherTimer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以看到，大的命名空间分类有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI&lt;/code&gt; 两类。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 是管理应用程序模型的，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是管理应用内 UI 的。小的命名空间分类有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Core&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Xaml&lt;/code&gt; 两类。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 是管理核心功能，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是管理 XAML UI 的。&lt;/p&gt;

&lt;h2 id=&quot;自顶向下&quot;&gt;自顶向下&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 到 XAML 内容，很明显地就能直到其是自顶向下的关系，应用内包含窗口，窗口内包含 XAML 内容。那么它们之间的关系呢？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 管理一个 UWP 应用中的所有视图（View），而 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 直接管理的视图是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;；也就是说，UWP 应用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 管理所有的应用视图 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;。而一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 包含一个窗口和一个线程调度模型，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-37-42.png&quot; alt=&quot;UWP 应用视图&quot; /&gt;&lt;br /&gt;
▲ UWP 应用视图&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/show-multiple-views-for-an-uwp-app&quot;&gt;让 UWP 应用显示多个窗口（多视图）&lt;/a&gt; 一文中，由于一个应用对应多个视图，所以可以更容易地理解它们之间的关系。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 就是我们所理解的窗口。为了方便使用，&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.XAML.Window&lt;/code&gt; 类型封装了这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt;。&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; 是基于消息循环的线程调度模型，正是因为有了消息循环，所以此窗口才能一直显示而不被销毁。&lt;/p&gt;

&lt;h2 id=&quot;对外还是对内&quot;&gt;对外，还是对内？&lt;/h2&gt;

&lt;p&gt;我们是站在 UWP 普通开发者的角度来思考这个问题的，普通 UWP 开发者是从 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 开始写 UWP 应用的。所以在这里，“外” 指的是页面之外，或者叫做我们直接编写的 XAML 内容之外，那些非 XAML 内容；而 “内” 指的是页面之内，也就是我们通常写的 XAML 内容。&lt;/p&gt;

&lt;p&gt;对外的部分有 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt;，对内的部分有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;。其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 的封装，提供了更多与 XAML 相关的功能。这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 也是这样，是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 的封装，提供了 XAML 相关的功能。&lt;/p&gt;

&lt;p&gt;具体来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 是与操作系统、与整个应用打交道的类型，提供了诸如窗口的尺寸、位置、输入状态等设置或调用；&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是与应用内 UI 打交道的类型，比如可以设置窗口内显示的 UI，设置内部哪个控件属于标题栏，获取此窗口内的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compositor&lt;/code&gt;。与之对应的，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 是应用与操作系统交互，与窗口消息循环机制协同工作的类型，包含窗口客户区和非客户区设置；&lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 也是与应用内 UI 打交道的类型，它可以使用 XAML 相关的类型对应用程序视图进行更方便的设置。&lt;/p&gt;

&lt;p&gt;总结起来，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 提供更加核心的操作系统或应用底层功能，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 对前者进行了封装，使得我们能够使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml&lt;/code&gt; 命名空间下的类型对窗口和应用视图进行控制。&lt;/p&gt;

&lt;h2 id=&quot;关于这些概念的更多应用&quot;&gt;关于这些概念的更多应用&lt;/h2&gt;

&lt;p&gt;我有另外一些文章用到了这些概念：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;从零开始创建一个 UWP 程序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/show-multiple-views-for-an-uwp-app&quot;&gt;让 UWP 应用显示多个窗口（多视图）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/tips-for-customize-uwp-title-bar&quot;&gt;UWP 扩展/自定义标题栏&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/shell/title-bar?wt.mc_id=MVP&quot;&gt;Title bar customization - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/core-application-window-of-uwp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/core-application-window-of-uwp.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>UWP CoreApplication / Application Vs CoreApplicationView / ApplicationView Vs CoreWindow / Window</title>
        <description>&lt;p&gt;I find a question on Stack Overflow &lt;a href=&quot;https://stackoverflow.com/a/51585979/6233938&quot;&gt;CoreApplicationView vs CoreWindow vs ApplicationView&lt;/a&gt;, so I write this post for the answer.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/core-application-window-of-uwp.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/core-application-window-of-uwp-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-namespace&quot;&gt;The namespace&lt;/h2&gt;

&lt;p&gt;Sometimes we have to view the full class names with namespaces to determine their meanings.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplication&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplicationView&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.Application&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Core.CoreWindow&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.Window&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extra, if you’re interested in the titlebar,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel.Core.CoreApplicationViewTitleBar&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.ViewManagement.ApplicationViewTitleBar&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extra, if you’re interested in the threading model,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Core.CoreDispatcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.DispatcherTimer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can split them into &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.ApplicationModel&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI&lt;/code&gt;, or split them into &lt;code class=&quot;highlighter-rouge&quot;&gt;Core&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Xaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; manage the application model, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; manage the application inner UI. The &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; manages the core functions, but the &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; manage the XAML UI.&lt;/p&gt;

&lt;h2 id=&quot;from-top-to-bottom&quot;&gt;From top to bottom&lt;/h2&gt;

&lt;p&gt;From top to bottom is from &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;, then to XAML. It’s obvious that the application contains windows and the window contains the inner XAML UI. Then, what’s the real relationship?&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; manages all the views of a UWP application and the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; is the view that it manages directly. A &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; contains a &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; as the window and a &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; as the threading model.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-37-42.png&quot; alt=&quot;UWP application view&quot; /&gt;&lt;br /&gt;
▲ UWP application view&lt;/p&gt;

&lt;p&gt;You can read &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views?wt.mc_id=MVP&quot;&gt;Show multiple views for an app - UWP app developer - Microsoft Docs&lt;/a&gt; to learn how to write multiple views applications. You’ll know more about the relationship between the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; is the window that we are all familiar with. 
&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.XAML.Window&lt;/code&gt; encapsulate the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; for easier usage. &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; is the threading model based on the windows message loop. It’s the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; that keeps the window to show all the time without being disposed.&lt;/p&gt;

&lt;h2 id=&quot;for-outer-or-for-inner&quot;&gt;For outer or for inner&lt;/h2&gt;

&lt;p&gt;Most UWP developers are normal developers, so we should stand on their side to think about the outer and the inner. Normal UWP developers start writing code from &lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt;, so the outer is out of the page and the inner is the XAML content of the page.&lt;/p&gt;

&lt;p&gt;The outer part contains &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; while the inner part contains &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;. Is it strange that the &lt;code class=&quot;highlighter-rouge&quot;&gt;Application&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; are the inner part? The reason is that they manage the XAML part of the application and the window.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; is the encapsulation of the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; to provide extra XAML UI functions. The same to the &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt;, it is the encapsulation of the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; providing extra XAML UI functions.&lt;/p&gt;

&lt;p&gt;In details, the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; is the class that interop with the Windows Operating System and the UWP application model. It provides those functions such as the window size, location, the input status, etc. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; is the class that provides the ability to use XAML UI for the window, such as setting the XAML content of the window, setting the titlebar of the window, or getting the &lt;code class=&quot;highlighter-rouge&quot;&gt;Compositor&lt;/code&gt; of the window. The &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; is the class that interop with the Windows Operating System and provides the mechanism of windows message loop and the ability to change the client area and the non-client area. The &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; is the same as the &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;, provides the ability to use XAML UI for the application.&lt;/p&gt;

&lt;p&gt;In conclusion, the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; provide the low-level core functions of the operating system and the application model. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; encapsulates them for XAML usage.&lt;/p&gt;

&lt;h2 id=&quot;some-usages-of-these-concepts&quot;&gt;Some usages of these concepts&lt;/h2&gt;

&lt;p&gt;I’ve written some other posts about UWP that take advantages of these concepts. Unfortunately, they are all not in English.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;Create a UWP application from zero.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/show-multiple-views-for-an-uwp-app&quot;&gt;Show multiple views for a UWP application&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/tips-for-customize-uwp-title-bar&quot;&gt;Extends the titlebar of a UWP application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/shell/title-bar?wt.mc_id=MVP&quot;&gt;Title bar customization - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/core-application-window-of-uwp-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/core-application-window-of-uwp-en.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>UWP 扩展/自定义标题栏的方法，一些概念和一些注意事项</title>
        <description>&lt;p&gt;在 Windows 10 的前几个版本中将页面内容扩展到标题栏上还算简单，主要是没什么坑。直到一些新控件的引入和一些外观设计趋势变化之后，扩展标题栏开始出现一些坑了。&lt;/p&gt;

&lt;p&gt;本文将重温 UWP 自定义标题栏或者扩展标题栏的方法，但更重要的是解决一些坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;扩展自定义标题栏&quot;&gt;扩展/自定义标题栏&lt;/h2&gt;

&lt;p&gt;要扩展标题栏，只需要拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 的实例，然后设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;TitleBar&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExtendViewIntoTitleBar&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CoreApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExtendViewIntoTitleBar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;要自定义标题栏，只需要拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 的实例，然后设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;TitleBar&lt;/code&gt; 里各种属性接口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;titleBar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;titleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BackgroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Khaki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;titleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ButtonBackgroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;一些概念&quot;&gt;一些概念&lt;/h2&gt;

&lt;p&gt;那么问题来了，为什么前者需要拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 的实例，后者需要拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 的实例？它们到底是什么区别？&lt;/p&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/core-application-window-of-uwp&quot;&gt;CoreApplication/Application、CoreWindow/Window 之间的区别&lt;/a&gt; 一文中提到过 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; 之间的关系。继续借用那篇文章中的图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-48-53.png&quot; alt=&quot;UWP 创建应用视图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 的封装，提供了更多与 XAML 相关的功能。这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 也是这样，是对 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 的封装，提供了 XAML 相关的功能。&lt;/p&gt;

&lt;p&gt;那篇文章中详细描述了这几个概念之间的关系和区别。考虑到阅读的一致性，我摘抄过来：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;具体来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 是与操作系统、与整个应用打交道的类型，提供了诸如窗口的尺寸、位置、输入状态等设置或调用；&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 是与应用内 UI 打交道的类型，比如可以设置窗口内显示的 UI，设置内部哪个控件属于标题栏，获取此窗口内的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compositor&lt;/code&gt;。与之对应的，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 是应用与操作系统交互，与窗口消息循环机制协同工作的类型，包含窗口客户区和非客户区设置；&lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 也是与应用内 UI 打交道的类型，它可以使用 XAML 相关的类型对应用程序视图进行更方便的设置。&lt;/p&gt;

  &lt;p&gt;总结起来，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 提供更加核心的操作系统或应用底层功能，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt; 对前者进行了封装，使得我们能够使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml&lt;/code&gt; 命名空间下的类型对窗口和应用视图进行控制。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，我们便能够理解为什么扩展标题栏和设置标题栏颜色会使用到两个不一样的类型了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ExtendViewIntoTitleBar&lt;/code&gt; 是改变了窗口的客户区（Client Area）和非客户区（Non-client Area）组成，这是传统 Win32 编程中的概念，是更接近操作系统底层的概念。&lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundColor&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ButtonBackgroundColor&lt;/code&gt; 这里需要用到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml&lt;/code&gt; 命名空间中的颜色，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 太底层，无法使用 XAML 颜色。&lt;/p&gt;

&lt;h2 id=&quot;一些坑&quot;&gt;一些坑&lt;/h2&gt;

&lt;h3 id=&quot;控件在标题栏区域无法交互&quot;&gt;控件在标题栏区域无法交互&lt;/h3&gt;

&lt;p&gt;想必当你扩展到标题栏后，在标题栏区域增加一些按钮的时候，肯定会遇到下面的情况：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-not-interactive.gif&quot; alt=&quot;控件的一半无法交互&quot; /&gt;&lt;br /&gt;
▲ 按钮在标题栏区域的一半无法交互&lt;/p&gt;

&lt;p&gt;这显然是无法接受的。&lt;/p&gt;

&lt;p&gt;然而，当我们将一个 XAML 控件指定为标题栏之后，就只会是那个控件所在的区域响应标题栏操作，其他地方就会恢复正常。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// TitleBar 是我在 XAML 中写的一个 x:Name=&quot;TitleBar&quot; 的控件。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-set-titlebar.gif&quot; alt=&quot;设置了一个标题栏&quot; /&gt;&lt;br /&gt;
▲ 按钮在标题栏区域现在可以交互了&lt;/p&gt;

&lt;p&gt;特别说明一下，&lt;code class=&quot;highlighter-rouge&quot;&gt;SetTitleBar&lt;/code&gt; 传入的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 类型的实例，也就是说这也是 XAML 交互的一部分。我们需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的实例，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;h3 id=&quot;更高的标题栏或者被遮挡&quot;&gt;更高的标题栏，或者被遮挡&lt;/h3&gt;

&lt;p&gt;如果被指定为标题栏的控件更大，超出标题栏区域了，它还会成为标题栏吗？如果被其他控件遮挡了，它还会响应标题栏事件吗？&lt;/p&gt;

&lt;p&gt;实际看来，无论它多大，都能响应标题栏事件；但被遮挡的部分就真的被遮挡了，没有标题栏响应。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-titlebar-been-covered.gif&quot; alt=&quot;更高的标题栏，或者被遮挡&quot; /&gt;&lt;br /&gt;
▲ 更高的标题栏，或者被遮挡&lt;/p&gt;

&lt;p&gt;事实上，指定为标题栏的控件可以在界面的任何地方，不需要一定在顶部。只不过，绝大多数不作死的应用都不会这样设置吧！&lt;/p&gt;

&lt;h3 id=&quot;在什么时机调用&quot;&gt;在什么时机调用？&lt;/h3&gt;

&lt;p&gt;扩展标题栏用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;，自定义标题栏颜色用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationView&lt;/code&gt;，将控件指定为标题栏用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;。如果我们的应用只有一个视图，其实我们随便找一个初始化的地方调用就好了。但如果我们的应用有多个视图，那么给非主要视图调用的时候就需要在其初始化之后了。阅读 &lt;a href=&quot;/post/show-multiple-views-for-an-uwp-app&quot;&gt;理解 UWP 视图的概念，让 UWP 应用显示多个窗口（多视图）&lt;/a&gt; 了解如何编写多个视图的 UWP 应用，了解非主要视图的初始化时机。&lt;/p&gt;

&lt;p&gt;当然，如果你比较极客，从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数开始写 UWP 应用，就像我在 &lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序&lt;/a&gt; 一文中做的一样，那么你也需要等到初始化完毕之后才能调用（至少是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindow&lt;/code&gt; 之后了）。&lt;/p&gt;

&lt;h2 id=&quot;适配移动设备&quot;&gt;适配移动设备&lt;/h2&gt;

&lt;p&gt;移动设备上并不是标题栏，而是状态了和虚拟按键。关于扩展视图到这些区域，可以阅读 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E6%A0%87%E9%A2%98%E6%A0%8F.html&quot;&gt;win10 uwp 标题栏 - 林德熙&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/shell/title-bar?wt.mc_id=MVP&quot;&gt;Title bar customization - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/tips-for-customize-uwp-title-bar.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/tips-for-customize-uwp-title-bar.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>理解 UWP 视图的概念，让 UWP 应用显示多个窗口（多视图）</title>
        <description>&lt;p&gt;UWP 应用多是一个窗口完成所有业务的，事实上我也推荐使用这种单一窗口的方式。不过，总有一些特别的情况下我们需要用到不止一个窗口，那么 UWP 中如何使用多窗口呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;为什么-uwp-需要多窗口&quot;&gt;为什么 UWP 需要多窗口？&lt;/h2&gt;

&lt;p&gt;多窗口在传统 Win32 的开发当中是司空见惯的事儿了，不过我个人非常不喜欢，因为 Windows 系统上的多窗口太多坑。以下是我以前写的关于传统多窗口开发中的一些坑（除此之外还有更多）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/fix-owner-window-dropping-down-when-close-a-modal-child-window&quot;&gt;关闭模态窗口后，父窗口居然跑到了其他窗口的后面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/wpf/2017/09/12/touch-not-work-in-wpf.html&quot;&gt;WPF 程序无法触摸操作？我们一起来找原因和解决方法！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用多窗口的原因很简单 —— 允许用户多任务处理。从这个角度来说，传统 Win32 使用“模态”多窗口的方式简直是低效的同时还带来 Bug！&lt;/p&gt;

&lt;p&gt;微软官方文档中列举了一些例子：例如一边写邮件一边参考以往的邮件；一边看正在播放的音乐一边浏览播放列表；一次性打开多份文章然后稍后一起阅读等。&lt;/p&gt;

&lt;h2 id=&quot;uwp-视图的概念&quot;&gt;UWP 视图的概念&lt;/h2&gt;

&lt;p&gt;在学习如何编写 UWP 多窗口之前，我们需要了解一些 UWP 视图（View）的概念。&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/core-application-window-of-uwp&quot;&gt;CoreApplication/Application、CoreWindow/Window 之间的区别&lt;/a&gt; 一文中，我描述了 UWP 视图的一些概念：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 管理一个 UWP 应用中的所有视图（View），而 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 直接管理的视图是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;；也就是说，UWP 应用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 管理所有的应用视图 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;。而一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 包含一个窗口和一个线程调度模型，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt;。&lt;/p&gt;

  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 就是我们所理解的窗口。为了方便使用，&lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.XAML.Window&lt;/code&gt; 类型封装了这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt;。&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; 是基于消息循环的线程调度模型，正是因为有了消息循环，所以此窗口才能一直显示而不被销毁。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序&lt;/a&gt; 一文中，我们也能体会到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 之间的关系，了解消息循环在应用中的作用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-37-42.png&quot; alt=&quot;UWP 应用视图&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;uwp-多窗口&quot;&gt;UWP 多窗口&lt;/h2&gt;

&lt;p&gt;在了解到 UWP 视图的概念之后，严格意义上说，这一节的标题应该叫做 “UWP 多视图”。&lt;/p&gt;

&lt;p&gt;我画了一个思维导图来描述它们之间的关系。&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 有静态方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateNewView&lt;/code&gt;，调用后能够创建新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;，这包含一个完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-48-53.png&quot; alt=&quot;UWP 创建应用视图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建并显示一个新 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt; 的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建一个 CoreApplicationView，即新的应用视图。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CoreApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateNewView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 一个应用视图有自己的 Id，稍后我们创建应用视图的时候，需要记录这个 Id。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newViewId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 使用新应用视图的 CoreDispatcher 线程调度模型来执行新视图中的操作。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreDispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 在新的应用视图中，我们将新的窗口内容设置为 ThePageInNewView 页面。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Navigate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThePageInNewView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Activate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 记录新应用视图的 Id，这样才能稍后切换。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newViewId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 使用刚刚记录的新应用视图 Id 显示新的应用视图。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewShown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationViewSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryShowAsStandaloneAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newViewId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;创建完后的效果如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-08-58-16.png&quot; alt=&quot;UWP 多窗口&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;管理多个-uwp-视图&quot;&gt;管理多个 UWP 视图&lt;/h2&gt;

&lt;p&gt;我们平时开发 UWP 应用的时候很少去关心 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;，因为默认情况下 UWP 能为我们做很多管理应用视图的工作。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainView&lt;/code&gt; 属性，即我们一开始运行 UWP 应用时的那个应用视图。如果我们有不止一个应用视图显示出来，那么这时点击主窗口的关闭按钮将不再是关闭，而是隐藏。如果要关闭，需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application.Exit&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Views&lt;/code&gt; 属性储存所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;，我们可以使用此集合来管理多个视图。使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationViewSwitcher.SwitchAsync&lt;/code&gt; 并传入视图 Id 可以切换视图的显示。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationViewSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SwitchAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewIdToShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views?wt.mc_id=MVP&quot;&gt;Show multiple views for an app - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/show-multiple-views-for-an-uwp-app.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/show-multiple-views-for-an-uwp-app.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>(2/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序</title>
        <description>&lt;p&gt;每次使用 Visual Studio 的模板创建一个 UWP 程序，我们会在项目中发现大量的项目文件、配置、应用启动流程代码和界面代码。然而这些文件在 UWP 程序中到底是如何工作起来的？&lt;/p&gt;

&lt;p&gt;我从零开始创建了一个 UWP 程序，用于探索这些文件的用途，了解 UWP 程序的启动流程。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文分为两个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-0&quot;&gt;从零开始创建一个 UWP 项目并完成部署&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-uwp-app-from-zero-1&quot;&gt;从零开始编写一个 UWP 应用程序和窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文将从 Main 函数开始，一步步跑起来一个应用程序，显示一个窗口，并在窗口中显示一些内容。重点在了解在 UWP 中运行应用程序，并显示窗口。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;启动应用&quot;&gt;启动应用&lt;/h2&gt;

&lt;p&gt;在上一篇文章中的末尾，我们成功启动了程序并进入了 Main 函数的断点，但实际上运行会报错。我们能看见一个窗口显示出来，随后提示进程已启动，但应用尚未运行。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Walterlv.Demo.ZeroUwp.exe process started, but the activation request failed with error ‘The app didn’t start’.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;是的，我们只有一个什么都没做的 Main 函数，进程当然能够成功启动；但我们需要能够启动应用。那么 UWP 的应用是什么呢？是 CoreApplication。&lt;/p&gt;

&lt;p&gt;所以我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt; 类型执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 静态方法。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-10-24-17.png&quot; alt=&quot;CoreApplication.Run&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此方法要求传入一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkViewSource&lt;/code&gt;。事实上 UWP 已经有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkViewSource&lt;/code&gt; 的实现了，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkViewSource&lt;/code&gt;。不过，我希望自己写一个，了解其原理。&lt;/p&gt;

&lt;p&gt;所以，就用 ReSharper 生成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkViewSource&lt;/code&gt; 的一个实现：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.ApplicationModel.Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.ZeroUwp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvViewSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFrameworkViewSource&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFrameworkView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WalterlvFrameworkView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkViewSource&lt;/code&gt; 接口中只有一个方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateView&lt;/code&gt;，返回一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkView&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;p&gt;只是写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt; 的异常，当然是跑不起来的，得返回一个真的 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFrameworkView&lt;/code&gt; 的实例。UWP 自带的实现为 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkView&lt;/code&gt;，那么我也自己实现一个。&lt;/p&gt;

&lt;p&gt;这次需要实现的方法会多一些：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.ApplicationModel.Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.UI.Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.ZeroUwp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvFrameworkView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFrameworkView&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreApplicationView&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreWindow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uninitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因此，我们需要理解这些方法的执行时机以及含义才能正确实现这些方法。庆幸的是，这些方法的含义都能在官方文档中找到（其实就是平时看到的注释）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview.initialize?wt.mc_id=MVP&quot;&gt;IFrameworkView.Initialize(CoreApplicationView)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview.load?wt.mc_id=MVP&quot;&gt;IFrameworkView.Load(String)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview.run?wt.mc_id=MVP&quot;&gt;IFrameworkView.Run&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview.setwindow?wt.mc_id=MVP&quot;&gt;IFrameworkView.SetWindow(CoreWindow)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview.uninitialize?wt.mc_id=MVP&quot;&gt;IFrameworkView.Uninitialize&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了方便查看，我将其整理到这些方法上作为注释。&lt;/p&gt;

&lt;p&gt;顺便的，下面这些方法刚好是按照应用生命周期的顺序被调用，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Initialize&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindow&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Load&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Uninitialize&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 当应用启动时将执行此方法。进行必要的初始化。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreApplicationView&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 每次应用需要显示一个窗口的时候，此方法就会被调用。用于为当前应用程序显示一个新的窗口视图。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreWindow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 会在 &amp;lt;see cref=&quot;Run&quot;/&amp;gt; 方法执行之前执行。如果需要使用外部资源，那么这时需要将其加载或激活。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 当此方法调用时，需要让应用内的视图（View）显示出来。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 当应用退出时将执行此方法。如果应用启动期间使用到了外部资源，需要在此时进行释放。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uninitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在此接口的所有方法留空地实现完以后，我们的 UWP 应用终于能跑起来了。当按下 F5 调试之后，不会再提示错误，而是依次执行这五个方法后，正常退出应用。&lt;/p&gt;

&lt;h2 id=&quot;启动窗口&quot;&gt;启动窗口&lt;/h2&gt;

&lt;p&gt;注意到以上所有方法都留空之后，应用程序很快就退出了。这与我们开发传统 Win32 应用时的效果是一致的 —— 是的，我们缺一个消息循环。我们需要一个不断处理的消息循环用来阻断主线程的退出，同时又能够不断响应消息。而这样的方法需要写到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run()&lt;/code&gt; 方法里面。&lt;/p&gt;

&lt;p&gt;UWP 中开启一个消息循环是非常容易的，不过我们需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; 对象。在我们目前的接口实现中，&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt; 对象可以从 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 中获取到。所以我们需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindow&lt;/code&gt; 方法中拿到 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 的实例，然后在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 中使用它开启窗口消息循环。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreWindow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Activate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreProcessEventsOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessUntilQuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CoreWindow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-15-19-57.png&quot; alt=&quot;开启消息循环&quot; /&gt;&lt;br /&gt;
▲ 开启了消息循环之后，应用不会直接退出了&lt;/p&gt;

&lt;p&gt;你可以通过阅读 &lt;a href=&quot;/post/show-multiple-views-for-an-uwp-app&quot;&gt;理解 UWP 视图的概念，让 UWP 应用显示多个窗口（多视图）&lt;/a&gt; 一文来了解 UWP 应用（&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplication&lt;/code&gt;）、应用视图（&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreApplicationView&lt;/code&gt;）、窗口（&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;）、线程调度模型（&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreDispatcher&lt;/code&gt;）之间的关系。&lt;/p&gt;

&lt;h2 id=&quot;在窗口中显示点东西&quot;&gt;在窗口中显示点东西&lt;/h2&gt;

&lt;p&gt;我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompositionAPI&lt;/code&gt; 可以在窗口中创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 并显示出来。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreWindow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateContainerVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateTargetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;compositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSpriteVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateColorBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromArgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0xFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertAtTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-20-44-19.png&quot; alt=&quot;窗口中新增的 Visual&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;在窗口中做一些交互&quot;&gt;在窗口中做一些交互&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CoreWindow&lt;/code&gt; 除了为我们提供了消息循环之外，也可以提供交互。监听 &lt;code class=&quot;highlighter-rouge&quot;&gt;PointerMoved&lt;/code&gt; 事件，我们可以做一些简单的交互。&lt;/p&gt;

&lt;p&gt;下面我用 Git 标准差异比较的方式添加了交互的代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;PointerMoved&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  public void SetWindow(CoreWindow window)
  {
      _window = window;
&lt;span class=&quot;gi&quot;&gt;+     _window.PointerMoved += OnPointerMoved;
&lt;/span&gt;  
      var compositor = new Compositor();
&lt;span class=&quot;gd&quot;&gt;-     var root = compositor.CreateContainerVisual();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+     _root = compositor.CreateContainerVisual();
&lt;/span&gt;      var compositionTarget = compositor.CreateTargetForCurrentView();
&lt;span class=&quot;gd&quot;&gt;-     compositionTarget.Root = _root;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+     compositionTarget.Root = _root;
&lt;/span&gt;  
      var child = compositor.CreateSpriteVisual();
      child.Size = new Vector2(100f, 100f);
      child.Brush = compositor.CreateColorBrush(Color.FromArgb(0xFF, 0x00, 0x80, 0xFF));
&lt;span class=&quot;gd&quot;&gt;-     root.Children.InsertAtTop(child);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+     _root.Children.InsertAtTop(child);
&lt;/span&gt;  }

+ private void OnPointerMoved(CoreWindow sender, PointerEventArgs args)
&lt;span class=&quot;gi&quot;&gt;+ {
+     var visual = _root.Children.First();
+     var position = args.CurrentPoint.Position;
+     visual.Offset = new Vector3((float) (position.X - 50f), (float) (position.Y - 50f), 0f);
+ }
&lt;/span&gt;
  private CoreWindow _window;
&lt;span class=&quot;gi&quot;&gt;+ private ContainerVisual _root;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;能够完成一些简单的交互。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-25-interaction.gif&quot; alt=&quot;窗口内的交互&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;特别注意-gc&quot;&gt;特别注意 GC&lt;/h2&gt;

&lt;p&gt;如果你真的按照以上步骤写出了一个 UWP 程序并且跑起来了，你会发现过一会儿就炸了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-28-19-40-03.png&quot; alt=&quot;ObjectDisposedException&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;compositor&lt;/code&gt; 我们是作为变量放到方法中的，很容易就被 GC 掉。最好将它放到字段中储存起来，避免被 GC。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Compositor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_compositor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;在本文中，我们了解到 UWP 的应用程序启动中也一样需要有窗口消息循环。不过 UWP 中创建消息循环还是非常简单的。&lt;/p&gt;

&lt;p&gt;我们使用 CompositionAPI 进行了一些界面显示和简单的交互。了解到即便是如此复杂的 UWP 程序，其启动流程也没有那么复杂。&lt;/p&gt;

&lt;p&gt;不过，如果你阅读了前面一篇 &lt;a href=&quot;/post/create-uwp-app-from-zero-0&quot;&gt;(1/2) 为了理解 UWP 的启动流程，我从零开始创建了一个 UWP 程序&lt;/a&gt;，会发现复杂的部分都在项目文件和系统的部分。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-uwp-app-from-zero-1.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-uwp-app-from-zero-1.html</guid>
        
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使窗口永不激活（No Activate 永不获得焦点）</title>
        <description>&lt;p&gt;有些窗口天生就是为了辅助其它程序而使用的，典型的如“输入法窗口”。这些窗口不希望抢夺其它窗口的焦点。&lt;/p&gt;

&lt;p&gt;有 Win32 方法来解决这样的问题，&lt;code class=&quot;highlighter-rouge&quot;&gt;WS_EX_NOACTIVATE&lt;/code&gt; 便是关键。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;具体来说，是给窗口样式中额外添加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;WS_EX_NOACTIVATE&lt;/code&gt; 位。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTheWindowHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exstyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_EXSTYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;SetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_EXSTYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exstyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_EX_NOACTIVATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，这里需要用到 P/Invoke 平台调用，可以阅读 &lt;a href=&quot;/post/pinvoke-net-visual-studio-extension&quot;&gt;使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名&lt;/a&gt; 了解快速生成平台调用方法签名的方法。&lt;/p&gt;

&lt;p&gt;于是，我们将完整的窗口代码写完，是下面这样。&lt;/p&gt;

&lt;p&gt;注意 64 位系统中需调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetWindowLongPtr&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowLongPtr&lt;/code&gt;，而 32 位系统中是没有这两个方法的；在任何版本的 Windows 中都是这样。当然，64 位系统会为其上运行的 32 位进程模拟 32 位系统的环境。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SourceInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WindowInteropHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exstyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_EXSTYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_EXSTYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exstyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_EX_NOACTIVATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Native&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Methods&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_EX_NOACTIVATE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x08000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GWL_EXSTYLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is64BitProcess&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is64BitProcess&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;GetWindowLong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;GetWindowLongPtr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWindowLong64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SetWindowLong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EntryPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SetWindowLongPtr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetWindowLong64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwNewLong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endregion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行这段代码，可以发现，即时我们的窗口中文本框获得了焦点，焦点其实依然在外面的程序中。（我们的文本框依然不会响应键盘输入的。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-no-activate.gif&quot; alt=&quot;No Activate&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/6804251/6233938&quot;&gt;c# - Not take focus, but allow interaction? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/winmsg/extended-window-styles?wt.mc_id=MVP&quot;&gt;Extended Window Styles - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/ms633585%28v=vs.85%29.aspx&quot;&gt;GetWindowLongPtr function (Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx&quot;&gt;SetWindowLongPtr function (Windows)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/no-activate-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/no-activate-window.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用 Microsoft.UI.Xaml 解决 UWP 控件和对老版本 Windows 10 的兼容性问题</title>
        <description>&lt;p&gt;虽然微软宣称 Windows 10 将是最后一个 Windows 版本，但由于年代跨越实在太久远，兼容性依然是避不开的问题。Microsoft.UI.Xaml 的预览版现已推出，旨在解决 UWP UI 控件在各个不同版本 Windows 上的兼容性问题。&lt;/p&gt;

&lt;p&gt;本文将简单了解一下 Microsoft.UI.Xaml 库，然后实际看看它的效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-10-的兼容性问题&quot;&gt;Windows 10 的兼容性问题&lt;/h2&gt;

&lt;p&gt;在创建 UWP 应用的时候，我们可以选择目标版本和最低版本。目标版本决定了我们能使用的最新 API，最低版本决定了我们需要支持的最低版本的 Windows 10。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-21-16-05.png&quot; alt=&quot;选择目标版本和最低版本&quot; /&gt;&lt;br /&gt;
▲ 图中目标版本为 17134，最低版本为 14393。事实上，目标版本必须是 17134，最低只能支持到 14393。&lt;/p&gt;

&lt;p&gt;然而，每一次新版本 Windows 10 的推出，都带来大量新的开发 API。可以去官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/updates-and-versions/choose-a-uwp-version?wt.mc_id=MVP&quot;&gt;Choose a UWP version - UWP app developer - Microsoft Docs&lt;/a&gt; 了解各个版本 Windows 10 新增的功能简介。&lt;/p&gt;

&lt;p&gt;微软在 Windows 10 16299 版本带来了 XAML 条件编译，用以在 XAML 中兼容不同版本的 Windows 10，然而这意味着必须选择 16299 作为最低 API 版本才能正常使用此功能。当然，XAML 条件编译还是带来了不少方便的特性呢，阅读 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-xaml-%E5%85%BC%E5%AE%B9%E5%A4%9A%E4%B8%AA%E7%89%88%E6%9C%AC%E6%9D%A1%E4%BB%B6%E7%BC%96%E8%AF%91.html&quot;&gt;win10 uwp xaml 兼容多个版本条件编译 - 林德熙&lt;/a&gt; 可以了解 XAML 条件编译的使用方法，顺便收获一只猫。&lt;/p&gt;

&lt;p&gt;Windows 10 也在各个版本新增了一些控件。那么问题来了，要支持最低版本就不能使用新控件。Windows 10 又不像 iOS 那样更新率高，意味着根本不能使用新控件进行开发。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-21-32-58.png&quot; alt=&quot;NavigationView&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;microsoftuixaml-库&quot;&gt;Microsoft.UI.Xaml 库&lt;/h2&gt;

&lt;p&gt;于是微软就推出了在 &lt;nuget.org&gt; 上推出了 NuGet 包 [Microsoft.UI.Xaml](https://www.nuget.org/packages/Microsoft.UI.Xaml)。&lt;/nuget.org&gt;&lt;/p&gt;

&lt;p&gt;使用此包，你需要将 UWP 的 &lt;strong&gt;目标版本设为 17134&lt;/strong&gt;，支持的 &lt;strong&gt;最低版本只能到 14393&lt;/strong&gt;，不能更低。&lt;/p&gt;

&lt;p&gt;官方对此包的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This package provides backward-compatible versions of Windows UI features including UWP XAML controls, and Fluent styles and materials. It is part of the Windows UI Library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即提供各种 Windows UI 功能的向后兼容性，包括 UWP XAML 控件、Fluent 流畅设计样式和画刷。当然，不支持亚克力效果的系统版本虽然画刷能用，不崩溃，但也没有效果的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-21-37-52.png&quot; alt=&quot;安装 Microsoft.UI.Xaml&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;microsoftuixaml-的上手方法&quot;&gt;Microsoft.UI.Xaml 的上手方法&lt;/h2&gt;

&lt;p&gt;安装 Microsoft.UI.Xaml 后，Visual Studio 会自动打开 readme.txt 文件提示我们用法：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks for installing the WinUI nuget package! Don’t forget to add this to your app.xaml:&lt;/p&gt;

  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Application.Resources&amp;gt;
    &amp;lt;XamlControlsResources xmlns=&quot;using:Microsoft.UI.Xaml.Controls&quot;/&amp;gt;
&amp;lt;/Application.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;See &lt;a href=&quot;http://aka.ms/winui&quot;&gt;http://aka.ms/winui&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;即我们需要在 App.xaml 文件中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;XamlControlsResources xmlns=&quot;using:Microsoft.UI.Xaml.Controls&quot;/&amp;gt;&lt;/code&gt; 作为应用程序的全局资源。不过，官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/toolkits/winui/getting-started?wt.mc_id=MVP&quot;&gt;Getting started with the Windows UI library&lt;/a&gt; 中有对此更详细的描述。&lt;/p&gt;

&lt;p&gt;如果我们是新 UWP 程序，这样写是没问题的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;XamlControlsResources&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;using:Microsoft.UI.Xaml.Controls&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application.Resources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但如果基于原有的程序进行兼容性改造，可能原 Application 中已经有资源了，就必须换一种写法：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;XamlControlsResources&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;using:Microsoft.UI.Xaml.Controls&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt; 
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application.Resources&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，以上这种改造在各种 XAML 上的行为都是一样的，比如我在 StackOverflow 上回答的问题 &lt;a href=&quot;https://stackoverflow.com/a/51391735/6233938&quot;&gt;Use ResourceDictionary with other Styles in WPF&lt;/a&gt; 也是这样的改法，其中说明了必须这样修改的原因。&lt;/p&gt;

&lt;p&gt;不过没有结束，在需要使用到新版本 Windows 10 控件的 XAML 文件中，需要添加命名空间前缀：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xmlns:controls=&quot;using:Microsoft.UI.Xaml.Controls&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样才能在 XAML 中使用 Microsoft.UI.Xaml 库中的新控件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;controls:NavigationView&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoView&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;controls:NavigationView.MenuItems&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListViewItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Home&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListViewItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Demo&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListViewItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;About&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ListViewItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/controls:NavigationView.MenuItems&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/controls:NavigationView&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还记得本文开头那张 Visual Studio 的兼容性提示图片吗？使用了 Microsoft.UI.Xaml 库之后，不会再有提示了。这不是欺骗，是真的具备了对早期系统的兼容性。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-20-59-27.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，一些广泛使用的 UWP 应用终于不用各种自己写控件来兼容低版本的 Windows 10 了。&lt;/p&gt;

&lt;p&gt;当然除了在 XAML 中，也可以在 C# 代码中使用库中的新 API。&lt;/p&gt;

&lt;h2 id=&quot;解决意料之外的错误&quot;&gt;解决意料之外的错误&lt;/h2&gt;

&lt;p&gt;一切可以那么顺利？不一定，你可能在刚刚把 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;XamlControlsResources /&amp;gt;&lt;/code&gt; 加入之后，就会发现程序启动即崩溃了……&lt;/p&gt;

&lt;p&gt;然后提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Runtime.InteropServices.COMException&lt;br /&gt;
  HResult=0x80004005&lt;br /&gt;
  Message=Error HRESULT E_FAIL has been returned from a call to a COM component.&lt;br /&gt;
  Source=&lt;Cannot evaluate=&quot;&quot; the=&quot;&quot; exception=&quot;&quot; source=&quot;&quot;&gt;  
  StackTrace:&lt;/Cannot&gt;&lt;/p&gt;
  &lt;Cannot evaluate=&quot;&quot; the=&quot;&quot; exception=&quot;&quot; stack=&quot;&quot; trace=&quot;&quot;&gt;
&lt;/Cannot&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-09-05-10.png&quot; alt=&quot;启动异常&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不得不说，微软再一次把内部错误暴露了出去。实际的错误原因是 —— &lt;strong&gt;目标 SDK 需要设置为 17134&lt;/strong&gt; —— 这是必须的！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-24-09-10-43.png&quot; alt=&quot;设置为 17134&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，这个版本号并不是跟随系统的，而是跟随 Microsoft.UI.Xaml 库的。库如果更新有新系统的控件，那么你更新库之后就需要再次更新目标 SDK 版本了。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/getting-started-with-microsoft-ui-xaml.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/getting-started-with-microsoft-ui-xaml.html</guid>
        
        
        <category>uwp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Windows 10 应用创建模糊背景窗口的三种方法</title>
        <description>&lt;p&gt;现代的操作系统中创建一张图片的高斯模糊效果非常容易，不过如果要在窗口中获得模糊支持就需要操作系统的原生支持了。iOS/Mac 和 Windows 系统都对此有支持。&lt;/p&gt;

&lt;p&gt;本文将介绍三种创建模糊背景窗口的方法。有人可能喜欢称之为毛玻璃窗口、亚克力窗口。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/create-blur-background-window.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/create-blur-background-window-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;最早我是在 StackOverflow 上回答一位网友的提问时写了一份非常长的答案，后来小伙伴建议我将答案改写成博客，于是我就改了。StackOverflow 上的答案在这里：&lt;a href=&quot;https://stackoverflow.com/a/51257595/6233938&quot;&gt;colors - WPF: How to Style my form with Transparency levels - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;三种创建模糊背景窗口的方法&quot;&gt;三种创建模糊背景窗口的方法&lt;/h2&gt;

&lt;p&gt;Windows 10 上创建带模糊背景的窗口有三种不同的方法，不过每一种都是既有好处又有坏处的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;调用 Win32 API —— &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt;，使用这种方式能够获得一个背景轻微透明的窗口。当然，如果需要模拟亚克力效果或者是 iOS/Mac 上的模糊效果就 gg 了。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-01-23-49-15.png&quot; alt=&quot;The image from my post&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;为窗口中的背景图片添加 WPF 自带的模糊效果 &lt;code class=&quot;highlighter-rouge&quot;&gt;BlurEffect&lt;/code&gt;。这种方式你想获得多大的模糊半径就能获得多大的模糊半径，不过带来的就是更高的性能损耗。同时，还得考虑在移动窗口的时候动态地去更新背景图片并再次模糊。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2018-07-16-19-08-19.png&quot; alt=&quot;BlurEffect of WPF&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;使用 Fluent Design System 中的亚克力效果 —— &lt;code class=&quot;highlighter-rouge&quot;&gt;AcrylicBrush&lt;/code&gt;。这绝对是 Windows 10 上获得背景模糊效果中视觉效果最好，同时又最省性能的方法了。不过，这种方法只能在 UWP 应用中使用。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2018-07-16-19-09-22.png&quot; alt=&quot;The UWP AcrylicBrush from docs.microsoft.com&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;setwindowcompositionattribute-api&quot;&gt;SetWindowCompositionAttribute API&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; 并没有那么好调用，所以我为此写了一个辅助类类封装对背景模糊效果的调用。使用这个辅助类，你只需要使用一行代码就能开启背景模糊效果。&lt;/p&gt;

&lt;p&gt;可以在 XAML 代码中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;interop:WindowBlur.IsEnabled=&quot;True&quot;&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:interop=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.Interop&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MainWindow&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;350&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;525&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;interop:WindowBlur.IsEnabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以在 cs 代码中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowBlur.SetIsEnabled(this, true)&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WindowBlur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetIsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowBlur&lt;/code&gt; 类准备了一个 GitHub Gist，在这里：&lt;a href=&quot;https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00&quot;&gt;https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00&lt;/a&gt;。你只需要将代码全部复制到你的项目中即可开始使用。&lt;/p&gt;

&lt;p&gt;当然，我还写了一篇博客专门讲使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; API 实现背景模糊效果：&lt;a href=&quot;/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&quot;&gt;在 Windows 10 上为 WPF 窗口添加模糊特效（就像开始菜单和操作中心那样）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;wpf-blureffect&quot;&gt;WPF BlurEffect&lt;/h2&gt;

&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Effect&lt;/code&gt; 属性，将其设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;BlurEffect&lt;/code&gt; 即可获得控件的高斯模糊效果。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MejirdrituTeWarqoudear.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;AllowsTransparency=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;WindowStyle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;None&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;540&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;360&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;YourImageFile.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-60&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image.Effect&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;BlurEffect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KernelType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gaussian&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Radius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image.Effect&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CornerRadius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F000000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;
                       &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                       &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;特别注意：&lt;strong&gt;此方法有严重地性能问题&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;如果你的窗口是一个异形窗口，例如是具有圆角的矩形，那么你需要额外为控件设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;RectangleGeometry&lt;/code&gt; 来裁剪控件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-16-19-09-43.png&quot; alt=&quot;Rounded Rectangle&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MejirdrituTeWarqoudear.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;540&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;360&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Clip&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;RectangleGeometry&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Rect=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30 30 480 300&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Clip&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;High+Sierra.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-60&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image.Effect&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;BlurEffect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KernelType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gaussian&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Radius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image.Effect&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F000000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果是圆形窗口，我另外写了一篇文章来说明进行圆形裁剪：&lt;a href=&quot;/post/clip-wpf-uielement-to-ellipse&quot;&gt;WPF 中使用附加属性，将任意 UI 元素或控件裁剪成圆形（椭圆）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;uwp-acyclicbrush&quot;&gt;UWP AcyclicBrush&lt;/h2&gt;

&lt;p&gt;微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic?wt.mc_id=MVP&quot;&gt;Acrylic material - UWP app developer - Microsoft Docs&lt;/a&gt; 讲解了如何使用亚克力效果。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-blur-background-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-blur-background-window.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>3 Ways to create a window with blurring background on Windows 10</title>
        <description>&lt;p&gt;This post is an answer from &lt;a href=&quot;https://stackoverflow.com/a/51257595/6233938&quot;&gt;Stack Overflow&lt;/a&gt; and introduce some methods to create a window with blurring background.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/create-blur-background-window.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/create-blur-background-window-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;options-to-blurring-background&quot;&gt;Options to blurring background&lt;/h2&gt;

&lt;p&gt;We have three ways to blurring background on Windows 10 and each has its advantages and disadvantages.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Call the Windows internal API &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt;. You can get a lightly blurred transparent Window but this transparency is much less than the iOS one.&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-01-23-49-15.png&quot; alt=&quot;The image from my post&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add a &lt;code class=&quot;highlighter-rouge&quot;&gt;BlurEffect&lt;/code&gt; to the window background image. You can get a more similar visual effect like the iOS one with very poor performance. But in this way, the background image is fixed and cannot be updated when the window moves.&lt;br /&gt;
&lt;img src=&quot;/static/posts/2018-07-16-19-08-19.png&quot; alt=&quot;BlurEffect of WPF&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use UWP instead of WPF and use the &lt;code class=&quot;highlighter-rouge&quot;&gt;AcrylicBrush&lt;/code&gt;. You can get a high-performance blur transparent window. But you should try the UWP Application development.&lt;br /&gt;
&lt;img src=&quot;/static/posts/2018-07-16-19-09-22.png&quot; alt=&quot;The UWP AcrylicBrush from docs.microsoft.com&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;setwindowcompositionattribute-api&quot;&gt;SetWindowCompositionAttribute API&lt;/h2&gt;

&lt;p&gt;Calling &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt; API is not very easy, so I’ve written a wrapper class for easier usage. You can use my class by writing only a simple line in the XAML file &lt;strong&gt;or&lt;/strong&gt; in the cs file.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:interop=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.Interop&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MainWindow&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;350&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;525&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;interop:WindowBlur.IsEnabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or you can use it in the cs file like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WindowBlur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetIsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just add my wrapper class into your project. It’s a very long class so I pasted into GitHub: &lt;a href=&quot;https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00&quot;&gt;https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also write a post for its usage, but it’s not in English: &lt;a href=&quot;https://walterlv.github.io/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&quot;&gt;https://walterlv.github.io/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;wpf-blureffect&quot;&gt;WPF BlurEffect&lt;/h2&gt;

&lt;p&gt;Just set the Effect property of a WPF UIElement.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MejirdrituTeWarqoudear.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;AllowsTransparency=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;WindowStyle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;None&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;540&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;360&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;YourImageFile.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-60&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image.Effect&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;BlurEffect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KernelType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gaussian&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Radius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image.Effect&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CornerRadius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F000000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;
                       &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                       &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that it has a very poor performance.&lt;/p&gt;

&lt;p&gt;You can also add a &lt;code class=&quot;highlighter-rouge&quot;&gt;RectangleGeometry&lt;/code&gt; to clip your UIElement into a rounded rectangle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-16-19-09-43.png&quot; alt=&quot;Rounded Rectangle&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MejirdrituTeWarqoudear.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;540&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;360&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Clip&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;RectangleGeometry&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RadiusY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Rect=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;30 30 480 300&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Clip&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;High+Sierra.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stretch=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fill&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-60&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image.Effect&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;BlurEffect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KernelType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gaussian&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Radius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image.Effect&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F000000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;uwp-acyclicbrush&quot;&gt;UWP AcyclicBrush&lt;/h2&gt;

&lt;p&gt;You can read Microsoft’s documents &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic?wt.mc_id=MVP&quot;&gt;Acrylic material - UWP app developer - Microsoft Docs&lt;/a&gt; for more details about how to write an &lt;code class=&quot;highlighter-rouge&quot;&gt;AcylicBrush&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-blur-background-window-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-blur-background-window-en.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>长期支持 LTS（Long-term Support）是怎样的一种支持方式</title>
        <description>&lt;p&gt;在 .NET Core 2.1 发布之时，微软称之为一个 LTS 版本，那么 LTS 的版本是一种怎样的版本呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;lts&quot;&gt;LTS&lt;/h2&gt;

&lt;p&gt;这是 .NET Core 2.1 的发布博客：&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/&quot;&gt;Announcing .NET Core 2.1 - .NET Blog&lt;/a&gt;；文中说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;.NET Core 2.1 will be a &lt;a href=&quot;https://github.com/dotnet/core/blob/master/microsoft-support.md&quot;&gt;long-term support (LTS)&lt;/a&gt; release. This means that it is supported for three years. We recommend that you make .NET Core 2.1 your new standard for .NET Core development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LTS 是 long-term support 的缩写，意为长期支持。&lt;/p&gt;

&lt;p&gt;这是基础库的开发者对库的使用者的一个承诺，保证某个版本的库发布之后的很长一段事件之内都得到支持。如果此版本发现一些紧急问题需要修复，那么就会在这个版本上进行更新。通常这些问题的修复都不会导致 API 变化（API 保证长期兼容），所以版本号的前两位是不变的，通常只变化第三位。&lt;/p&gt;

&lt;p&gt;微软对 .NET Core 的长期支持策略有两种支持的时长：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;某个 release 版本发布之后三年；&lt;/li&gt;
  &lt;li&gt;后续替代此 release 的另一个新的 release 发布之后一年&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果某个库承诺进行长期支持，那么至少数年之内使用这个库都是安全的。这段时间也足够多数开发者进行新库的准备和升级了。&lt;/p&gt;

&lt;h2 id=&quot;ltsc--ltsb&quot;&gt;LTSC / LTSB&lt;/h2&gt;

&lt;p&gt;对于长期支持还有其他的变种名称，当然也对应着不同的功能。&lt;/p&gt;

&lt;p&gt;例如 Windows 操作系统使用的 LTSC（Long-Term Servicing Channel）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/system-center/ltsc-and-sac-overview#long-term-servicing-channel-ltsc?wt.mc_id=MVP&quot;&gt;Overview of System Center LTSC and SAC releases - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/get-started/semi-annual-channel-overview#long-term-servicing-channel-ltsc?wt.mc_id=MVP&quot;&gt;Windows Server Semi-Annual Channel overview - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/get-started/windows-server-release-info?wt.mc_id=MVP&quot;&gt;Windows Server release information - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;还有已经不怎么使用的 LTSB（Long-Term Servicing Branch）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.technet.microsoft.com/surface/2017/04/11/documentation-updates-for-surface-and-windows-10-ltsb-compatibility/&quot;&gt;Documentation Updates for Surface and Windows 10 LTSB Compatibility – Surface&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;esr&quot;&gt;ESR&lt;/h2&gt;

&lt;p&gt;当然也有机构采用 ESR 作为长期支持版本的称呼，ESR 全称为 Extended Support Release。&lt;/p&gt;

&lt;p&gt;例如 Firefox 的长期支持版本：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.mozilla.org/media/img/firefox/organizations/release-overview.d8ca18efe06f.png&quot; alt=&quot;Firefox ESR Release Overview&quot; /&gt;&lt;br /&gt;
▲ Firefox ESR Release Overview&lt;/p&gt;

&lt;p&gt;详见：&lt;a href=&quot;https://www.mozilla.org/en-US/firefox/organizations/&quot;&gt;Firefox Extended Support Release for Your Organization, Business, Enterprise — Mozilla&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/what-is-long-term-support.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/what-is-long-term-support.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在制作跨平台的 NuGet 工具包时，如何将工具（exe/dll）的所有依赖一并放入包中</title>
        <description>&lt;p&gt;NuGet 提供了工具类型的包支持，生成一个基于 .NET Core 的 dll 或者基于 .NET Framework 的 exe 之后，你几乎可以对项目做任何事情。但是，默认情况下，NuGet 不会将这些工具的依赖一起打包进入 NuGet 包 nupkg 文件内，这就使得功能比较复杂的跨平台 NuGet 工具包几乎是无法正常工作的。&lt;/p&gt;

&lt;p&gt;本文将介绍将这些依赖加入 NuGet 包中的方法，使得复杂的工具能够正常使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;你可能是在 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;创建一个基于命令行工具的跨平台 NuGet 工具包&lt;/a&gt; 的时候遇到依赖问题的，也可能是自己做到另外什么工具遇到的。&lt;/p&gt;

&lt;p&gt;典型的例子，我正在做一个基于 Roslyn 的 NuGet 工具包。于是整个 Roslyn 的大量 dll 都是我的依赖。但默认情况下，打出来的包并不包含 Roslyn 相关的 dll。&lt;/p&gt;

&lt;h2 id=&quot;探索&quot;&gt;探索&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/nuget/&quot;&gt;官方关于 NuGet 的文档&lt;/a&gt; 并没有提及任何关于额外添加依赖文件的方法，擅长 NuGet 的大神 &lt;a href=&quot;https://natemcmaster.com/&quot;&gt;Nate McMaster&lt;/a&gt; 虽然有一篇关于加入 NuGet 依赖的博客 &lt;a href=&quot;https://natemcmaster.com/blog/2017/11/11/msbuild-task-with-dependencies/?wt.mc_id=MVP&quot;&gt;MSBuild tasks with dependencies&lt;/a&gt;，但依然没有很简单地解决。&lt;/p&gt;

&lt;p&gt;尝试找一个实际将这些依赖 Include 进来，但是不知道什么时机合适。太早了依赖文件还没有生成，太晚了 NuGet 包中即将打的文件早已确认，Include 了也没用。&lt;/p&gt;

&lt;p&gt;于是，我去阅读了 Microsoft.NET.Sdk 的源码，找到了并没有公开的内部方法来解决这个问题。关于阅读 Microsoft.NET.Sdk 源码的方式，可以参考 &lt;a href=&quot;/post/read-microsoft-net-sdk&quot;&gt;解读 Microsoft.NET.Sdk 的源码，你能定制各种奇怪而富有创意的编译过程&lt;/a&gt; 和 &lt;a href=&quot;/post/read-microsoft-net-sdk-en&quot;&gt;Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;NuGet 打包的核心在 NuGet.Build.Tasks.Pack.targets 文件，主要是这段代码（省略了大量内容，留下了看起来有点儿关系的部分）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 已删减大量内容，全部内容请自己阅读源码。 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackTask&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackItem=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackProjectInputFile)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageFiles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_PackageFiles)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageFilesToExclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(_PackageFilesToExclude)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PackageTypes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(PackageType)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IsTool=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IsTool)&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IncludeBuildOutput=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeBuildOutput)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PackageTypes&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsTool&lt;/code&gt; 是我放来灌水的，这两个属性决定了我们打出来的包的类型（是否是工具类型）。看起来像的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageFiles&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;PackageFilesToExclude&lt;/code&gt; 属性，不过这两个属性用到了私有的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFiles)&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFilesToExclude)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以接下来需要搜索到底是那里在为 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFiles)&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(_PackageFilesToExclude)&lt;/code&gt; 赋值。搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;_PackageFiles&lt;/code&gt; 可以找到赋值的地方就在 NuGet.Build.Tasks.Pack.targets 文件中：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 已删减大量内容，全部内容请自己阅读源码。 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_GetPackageFiles&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IncludeContentInPack) == 'true'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_PackageFilesToExclude&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Content)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;'%(Content.Pack)' == 'false'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Include PackageFiles and Content of the project being packed --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_PackageFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Content)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; %(Content.Pack) != 'false' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildAction&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition =&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;'%(Content.BuildAction)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildAction&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_PackageFiles&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_PackageFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(Compile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; %(Compile.Pack) == 'true' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildAction&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition =&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;'%(Compile.BuildAction)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Compile&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildAction&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_PackageFiles&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;_PackageFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(None)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; %(None.Pack) == 'true' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;BuildAction&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition =&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;'%(None.BuildAction)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;None&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BuildAction&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/_PackageFiles&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 已删减大量内容，全部内容请自己阅读源码。 --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是一个私有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，所以答案已经呼之欲出了。&lt;/p&gt;

&lt;h2 id=&quot;答案&quot;&gt;答案&lt;/h2&gt;

&lt;p&gt;我们写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;_GetPackageFiles&lt;/code&gt; 设为我们的前置 Target。然后，我们就可以把输出目录中除了 NuGet 自然而然会帮我们打入 NuGet 包中的所有文件都加入到 NuGet 包中的对应目录下。&lt;/p&gt;

&lt;p&gt;具体来说，是将下面的 Target 添加到项目文件的末尾。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IncludeAllDependencies&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_GetPackageFiles&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)*.*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)$(AssemblyName).exe;$(OutputPath)$(AssemblyName).pdb&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tools\net47&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/include-dependencies-into-nuget-tool-package.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/include-dependencies-into-nuget-tool-package.html</guid>
        
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 LINQ to XML，.NET 让生成 XML 文件变得和直接写 XML 一样轻松</title>
        <description>&lt;p&gt;由 .NET Framework 3.5 引入，并依然在 .NET Core 中发扬光大的 LINQ to XML 让编写 XML 文件变得非常轻松。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;XElement&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;XAttribute&lt;/code&gt; 我们能够完整构造一个 XML 出来。为了能直观地体会到优势，我写一个最简单的例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Attribute&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Node&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;构造出来的 XML 将是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Root&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Attribute=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Node&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Node&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Root&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是不是觉得包括行的安排和缩进在内，都和 XML 一样简单？&lt;/p&gt;

&lt;p&gt;我们来看一个更复杂的例子，这是直接在编写一个 NuGet 的 nuspec 文件：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlns&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;package&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Xmlns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MSTestEnhancer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.6.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;authors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;owners&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requireLicenseAcceptance&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;licenseUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/dotnet-campus/MSTestEnhancer/blob/master/LICENSE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;projectUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://dotnet-campus.github.io/mstest-enhancer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;iconUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://dotnet-campus.github.io/mstest-enhancer/icon.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MSTestEnhancer helps you to write unit tests without naming any method. You can write method contract descriptions instead of writing confusing test method name when writing unit tests.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;releaseNotes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Support passing null into WithArgument method.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;copyright&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Copyright (c) 2018 dotnet职业技术学院&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;repository&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/dotnet-campus/MSTestEnhancer.git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;targetFramework&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dependency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;exclude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;C:\Users\walterlv\Desktop\Walterlv.Demo.nuspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dependencies&lt;/code&gt; 集合我写在了其他地方，这样更像是动态生成，而不是仅仅为了给一个例子。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4.4.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETStandard2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;生成的 nuspec 文件非常像 NuGet 的原生 nuspec 文件。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;metadata&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;MSTestEnhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;owners&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/owners&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;requireLicenseAcceptance&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/requireLicenseAcceptance&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;licenseUrl&amp;gt;&lt;/span&gt;https://github.com/easiwin/MSTestEnhancer/blob/master/LICENSE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/licenseUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;projectUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/projectUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;iconUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer/icon.png&lt;span class=&quot;nt&quot;&gt;&amp;lt;/iconUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;MSTestEnhancer helps you to write unit tests without naming any method. You can write method contract descriptions instead of writing confusing test method name when writing unit tests.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;releaseNotes&amp;gt;&lt;/span&gt;Support passing null into WithArgument method.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/releaseNotes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;Copyright (c) 2018 dotnet职业技术学院&lt;span class=&quot;nt&quot;&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;repository&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/easiwin/MSTestEnhancer.git&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETStandard2.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/metadata&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-vs-dom?wt.mc_id=MVP&quot;&gt;LINQ to XML 与DOM (C#) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/how-to-control-namespace-prefixes-linq-to-xml?wt.mc_id=MVP&quot;&gt;如何：控制命名空间前缀 (C#) (LINQ to XML) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-xml-using-linq-to-xml.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-xml-using-linq-to-xml.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>PasswordVault —— 在 UWP 应用中安全地保存密码</title>
        <description>&lt;p&gt;只要你做过自动登录，一定会遇到密码的安全问题。现在大部分的网络服务都已经支持 Token 了，有些已经支持 OAuth2.0，这意味着客户端不怎么需要关心密码的安全保存问题。&lt;/p&gt;

&lt;p&gt;但是，依然还有一些古老的服务和协议需要直接传输密码，比如邮件的 IMAP 协议。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我在 &lt;a href=&quot;https://github.com/walterlv/ERMail&quot;&gt;ERMail&lt;/a&gt; 应用的开发中就遇到了这样的问题，作为一款邮件客户端，IMAP 协议下的自动登录依然要在用户的本地保存密码。&lt;/p&gt;

&lt;p&gt;无论你采用哪一种加密协议保存用户的密码，由于客户端与黑客的信息量都是相同的，所以客户端能解密出来黑客就一定能解密出来。所以，单纯地依靠应用自身是无法完成安全的密码保存的，利用操作系统、密码服务器或者其他硬件作为中转是一定需要采用的方案。&lt;/p&gt;

&lt;p&gt;后两者的成本较高，采用操作系统自带的凭据管理器是成本较低的方案。于是我找到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;PasswordVault&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;微软官网对 &lt;code class=&quot;highlighter-rouge&quot;&gt;PasswordVault&lt;/code&gt; 有全面的介绍：&lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordvault&quot;&gt;PasswordVault Class (Windows.Security.Credentials) - UWP app developer - Microsoft Docs&lt;/a&gt;，使用起来也是非常简单的。通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add(PasswordCredential)&lt;/code&gt; 方法完成密码的保存，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Retrieve(String, String?wt.mc_id=MVP)&lt;/code&gt; 完成密码的读取。&lt;/p&gt;

&lt;p&gt;每一个 UWP 应用之间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PasswordVault&lt;/code&gt; 是独立且互相不可访问的，普通用户也无法直接获取到密码；对于黑客，如果无法黑掉用户账户，也是无法解密出密码的，所以在一般使用场景下，安全性是够的。&lt;/p&gt;

&lt;p&gt;如果需要保存密码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordVault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordCredential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.Uwp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t^vxR1kuR7@7*zZh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 是保存的用户名，&lt;code class=&quot;highlighter-rouge&quot;&gt;t^vxR1kuR7@7*zZh&lt;/code&gt; 是保存的密码。&lt;/p&gt;

&lt;p&gt;如果需要获取此前保存的密码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordVault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Retrieve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.Uwp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;得到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;password&lt;/code&gt; 即是密码字符串 &lt;code class=&quot;highlighter-rouge&quot;&gt;t^vxR1kuR7@7*zZh&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;https://github.com/walterlv/ERMail&quot;&gt;ERMail&lt;/a&gt; 中，考虑到多数代码是跨平台的，所以我使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;IPasswordManager&lt;/code&gt; 接口来隔离这种 UWP 平台特定的方法。于是 &lt;a href=&quot;https://github.com/walterlv/ERMail&quot;&gt;ERMail&lt;/a&gt; 的 UWP 版本的密码管理实现就像如下这么简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Windows.Security.Credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.ERMail.Mailing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.ERMail.Utils&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PasswordManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPasswordManager&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MailVaultResourceName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.ERMail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPasswordManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPasswordManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Retrieve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordVault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Retrieve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MailVaultResourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPasswordManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordVault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PasswordCredential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MailVaultResourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/uwp-password-vault.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/uwp-password-vault.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>Windows 10 四月更新，文件夹名称也能区分大小写了</title>
        <description>&lt;p&gt;Linux 一向都是区分文件和文件夹大小写的。Mac OS 默认不区分文件和文件夹大小写，不过可以配置成支持。而 Windows 向来是不区分文件和文件夹大小写的，但是从 NTFS 开始却又支持区分文件夹大小写。&lt;/p&gt;

&lt;p&gt;本文将介绍 Windows 10 四月更新带来的新特性 —— 让文件夹名称也能区分大小写。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题&quot;&gt;问题&lt;/h2&gt;

&lt;p&gt;本来文件系统是否区分大小写只是单纯风格上的差异，并没有孰优孰劣，但这可让那些跨平台的文件系统难以抉择了。典型的例子就是 Git。&lt;/p&gt;

&lt;p&gt;我曾经就遭遇过 Git 操作上的大小写敏感性问题，写了一篇博客：&lt;a href=&quot;/post/case-insensitive-in-git-rename&quot;&gt;解决 Git 重命名时遇到的大小写不敏感的问题&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;由于 Windows 文件系统对大小写不敏感，所以上面的问题才变得尤其难办，竟然需要通过至少两次提交，并且丢掉单线的 Git 历史记录的方式才能真正完成任务。而单纯让 Git 在仓库中区分大小写竟然会产生两份文件（却无法在 Windows 系统中观察到）。&lt;/p&gt;

&lt;h2 id=&quot;开启方法&quot;&gt;开启方法&lt;/h2&gt;

&lt;p&gt;Windows 10 四月更新终于带来了文件夹区分大小写的支持！&lt;/p&gt;

&lt;p&gt;使用管理员权限在当前文件夹启动 PowerShell：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-10-43-02.png&quot; alt=&quot;管理员权限启动 PowerShell&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;fsutil.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetCaseSensitiveInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\walterlv\GitDemo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是的，就是上面这一段非常简单而容易理解的命令即可开启&lt;strong&gt;单个文件夹&lt;/strong&gt;的名称区分大小写功能。只是单个文件夹！如果需要开启其他文件夹，需要多次执行这样的命令。&lt;/p&gt;

&lt;p&gt;而如果需要关闭对此文件夹的大小写支持，只需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;enable&lt;/code&gt; 改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;disable&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;fsutil.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SetCaseSensitiveInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\walterlv\GitDemo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-14-09-07-45.png&quot; alt=&quot;区分大小写的效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看！以上就是在 Windows 10 系统级开启了大小写敏感的我的一个 Git 仓库，这下可以让跨平台的 Git 工作起来在各个系统都一样了。&lt;/p&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;

&lt;p&gt;以上命令的正确运行需要以下条件，缺一不可：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Windows 10 四月更新（1803）&lt;/li&gt;
  &lt;li&gt;安装有 Linux 子系统，即 Windows Subsystem for Linux&lt;/li&gt;
  &lt;li&gt;所在分区为 NTFS 格式&lt;/li&gt;
  &lt;li&gt;以管理员权限运行 PowerShell&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果没有安装 Linux 子系统，那么运行时会出现以下错误：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;错误：不支持该请求。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 中文版 PowerShell&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;supported.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 英文版 PowerShell&lt;/p&gt;

&lt;p&gt;这个问题在 MicrosoftDocs 的 GitHub 仓库中被提到了：&lt;a href=&quot;https://github.com/MicrosoftDocs/windowsserverdocs/issues/977&quot;&gt;fsutil setCaseSensitiveInfo · Issue #977 · MicrosoftDocs/windowsserverdocs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;安装 Linux 子系统的方法可以参考微软官方文档：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/windows/wsl/install-win10&quot;&gt;Install Windows Subystem for Linux (WSL) on on Windows 10&lt;/a&gt;。如果英文阅读有压力，可以参考毒逆天的博客：&lt;a href=&quot;https://www.cnblogs.com/dunitian/p/9159897.html?wt.mc_id=MVP&quot;&gt;Win10 安装 Linux子系统 Ubuntu18.04 / Kali Linux 的体验&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;简单点，就是管理员权限 PowerShell 敲个命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Enable-WindowsOptionalFeature&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Online&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FeatureName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Microsoft-Windows-Subsystem-Linux&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者命令都懒得敲，就是去商店下载（在商店搜索 Linux）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-30-20-31-54.png&quot; alt=&quot;应用商店下载安装 Linux&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;附&quot;&gt;附&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fsutil file&lt;/code&gt; 支持的命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;⚡&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv.github.io&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fsutil.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Supported&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createNew&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Creates&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findBySID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;information&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;available&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;optimizeMetadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Optimize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryAllocRanges&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;allocated&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryCaseSensitiveInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sensitive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;information&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryExtents&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;extents&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryExtentsAndRefCounts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;extents&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;their&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;corresponding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;refcounts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryFileID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Queries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specified&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryFileNameById&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Displays&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryOptimizeMetadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;optimize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queryValidData&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Queries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;valid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setCaseSensitiveInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sensitive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;information&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setShortName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setValidData&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;valid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setZeroData&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setEOF&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Sets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setStrictlySequential&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Sets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReFS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SMR&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strictly&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sequential&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;fsutil 支持的命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;⚡&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;walterlv.github.io&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fsutil.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Supported&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;8dot3name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;8dot3name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;behavior&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Control&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;behavior&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dax&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Dax&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Manage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dirty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;specific&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fsInfo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;information&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hardlink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Hardlink&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;objectID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;quota&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Quota&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repair&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;healing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reparsePoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Reparse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Transactional&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Manager&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sparse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Sparse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tiering&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tiering&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Transaction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Volume&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Transparent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hosting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;management&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MicrosoftDocs/windowsserverdocs/issues/977&quot;&gt;fsutil setCaseSensitiveInfo · Issue #977 · MicrosoftDocs/windowsserverdocs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/50839623/6233938&quot;&gt;windows - fsutil - The request is not supported after setCaseSensitiveInfo - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/case-sensitive-in-windows-file-system.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/case-sensitive-in-windows-file-system.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何使用 MSBuild Target（Exec）中的控制台输出</title>
        <description>&lt;p&gt;我曾经写过一篇文章 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;，通过编写一个控制台程序来参与编译过程。但是，相比于 &lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;基于 Task 的方式&lt;/a&gt;，可控制的因素还是太少了。&lt;/p&gt;

&lt;p&gt;有没有什么办法能够让控制台程序也能与 MSBuild Target 之间发生更多的信息交换呢？答案是有的，通过捕获控制台的输出！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;捕获控制台输出&quot;&gt;捕获控制台输出&lt;/h2&gt;

&lt;p&gt;如果你喜爱阅读文档，那么答案已经不陌生了，在微软的官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/exec-task?wt.mc_id=MVP&quot;&gt;Exec Task&lt;/a&gt; 中就已经提及了属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConsoleToMSBuild&lt;/code&gt;。将此属性设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;True&lt;/code&gt;，将能够捕获控制台输出到 MSBuild 中。（&lt;em&gt;不过据说典型的程序员是不爱看文档的&lt;/em&gt;）&lt;/p&gt;

&lt;p&gt;那么，捕获的输出去了哪里呢？&lt;/p&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt; 中提到了使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Output&lt;/code&gt; 来将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 中的参数输出出来。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exec&lt;/code&gt; 也是这么做的。我们将 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConsoleOutput&lt;/code&gt; 输出出来即可。由于这个属性不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ITaskItem[]&lt;/code&gt; 类型的，所以我们只能得到字符串属性，于是只能通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyName&lt;/code&gt; 来接收这样的输出。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ConsoleToMSBuild=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;quot;$(NuGetWalterlvToolPath)&amp;amp;quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ConsoleOutput&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OutputOfTheCommand&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Exec&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;propertygroup-转-itemgroup&quot;&gt;PropertyGroup 转 ItemGroup&lt;/h2&gt;

&lt;p&gt;如果你需要的只是一个字符串，那看完上一节就已经够了。但如果你希望得到的是一组值（例如新增了一组需要编译的文件），那么需要得到的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 中的多个值，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 中的单个值。（如果不太明白 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 之间的差别，不要紧，可以阅读 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;。）&lt;/p&gt;

&lt;p&gt;MSBuild 还自带了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;，名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateItem&lt;/code&gt;，就是从一段字符串创建一组 &lt;code class=&quot;highlighter-rouge&quot;&gt;Item&lt;/code&gt;。通过下面这段代码，我们能将上一节捕获到的属性转换成项的集合。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputOfTheCommand)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Include&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdditionalCompile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateItem&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，我们便能够&lt;/p&gt;

&lt;p&gt;更加完整的代码可能更具有参考意义，所以我贴在了下面：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenerateAdditionalCode&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ConsoleToMSBuild=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;quot;$(NuGetWalterlvToolPath)&amp;amp;quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ConsoleOutput&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PropertyName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OutputOfTheCommand&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Exec&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_IncludeGeneratedAdditionalCode&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenerateAdditionalCode&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CreateItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputOfTheCommand)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Output&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TaskParameter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Include&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ItemName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdditionalCompile&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/CreateItem&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@(AdditionalCompile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;额外添加的编译文件：@(AdditionalCompile)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;createitem-的转换分隔符&quot;&gt;CreateItem 的转换分隔符&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CreateItem&lt;/code&gt; 从属性或字符串转到项是根据分隔符来区分的。由于使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(Item)&lt;/code&gt; 来获取项时，会得到一个用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 分隔的字符串，所以不难想到我们控制台输出的字符串使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 分隔即能满足我们的转换需求。&lt;strong&gt;但事实上这是不行的！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;因为控制台的转换，每行是有缓冲区限制的，也就是说单行字数不能过多，否则会自动加换行符——这可能导致我们转换成的某一项或者多项中间带了换行符，从而导致错误。&lt;/p&gt;

&lt;p&gt;于是，建议直接在控制台程序中使用换行符本身作为分隔符，这样便可以去除这样的限制。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateItem&lt;/code&gt; 也是支持换行符分隔的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/8938679/6233938&quot;&gt;How get exec task output with msbuild - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/exec-task?wt.mc_id=MVP&quot;&gt;Exec Task - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/7909825/6233938&quot;&gt;Empty an MSBuild ItemGroup - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/what-s-new-in-msbuild-15-0#updates?wt.mc_id=MVP&quot;&gt;What’s New in MSBuild 15 - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/item-element-msbuild?wt.mc_id=MVP&quot;&gt;Item Element (MSBuild) - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/exec-task-of-msbuild-target.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/exec-task-of-msbuild-target.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>微软 Windows 系统检测网络连通性（用于显示感叹号）竟然是通过访问一个特殊网址来实现的</title>
        <description>&lt;p&gt;一次我走到了弱网环境中，意外地发现浏览器打开了 &lt;a href=&quot;http://www.msftconnecttest.com/redirect&quot;&gt;http://www.msftconnecttest.com/redirect&lt;/a&gt; 网址，随后右下角的网络图标出现了一枚“感叹号”。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;吹水的推断&quot;&gt;吹水的推断&lt;/h2&gt;

&lt;p&gt;从直观看来，这个网址的连通性和网络图标上的“感叹号”有着直接的联系。那么到底有没有联系呢？于是去知乎上看看，发现了&lt;a href=&quot;https://www.zhihu.com/question/59865134/answer/169818796&quot;&gt;专业造轮子拉黑抢前排的轮子哥的回复&lt;/a&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;vczh&lt;/strong&gt; 专业造轮子，拉黑抢前排。http://gaclib.net&lt;/p&gt;

  &lt;p&gt;这个网站是windows用来测试你有没有连上网的（&lt;br /&gt;
编辑于 2017-05-15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而轮子哥是谁呢？Microsoft Office 团队的开发人员，所以对微软产品的一些技术性描述还是有些可信的（虽然吹水占了多数）。轮子哥不要怪我啊（逃&lt;/p&gt;

&lt;p&gt;不过，吹水归吹水，还是需要更多地了解下这个网址。&lt;/p&gt;

&lt;h2 id=&quot;官方的依据&quot;&gt;官方的依据&lt;/h2&gt;

&lt;p&gt;这个网址用于检测网络连接状态，并以图标形式展示给用户。而这个图标称之为“网络连接状态图标”（Connection Status Icon，NCSI）。&lt;/p&gt;

&lt;p&gt;自 Windows 8 开始，不同版本的 Windows 操作系统有不同的检测网络连接状态的 url，但都是通过 url 来检测的。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Windows 10.0.15063 (1703) 至 10.0.07134 (1803)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.msftconnecttest.com/redirect&quot;&gt;http://www.msftconnecttest.com/redirect&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 10.0.14393 (1607)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.msftconnecttest.com/connecttest.txt&quot;&gt;http://www.msftconnecttest.com/connecttest.txt&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 8/8.1 至 10.0.15063 (1511)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.msftncsi.com/ncsi.txt&quot;&gt;http://www.msftncsi.com/ncsi.txt&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而 Windows XP/Vista/7 的检测方式有些不同，但都是通过 Network Location Awareness (NLA) 方式来检测的。&lt;/p&gt;

&lt;p&gt;关于 Windows XP/Vista/7 的检测方式，可以阅读：&lt;a href=&quot;https://blogs.technet.microsoft.com/networking/2010/09/08/network-location-awareness-nla-and-how-it-relates-to-windows-firewall-profiles/&quot;&gt;Network Location Awareness (NLA) and how it relates to Windows Firewall Profiles - Networking Blog&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;局限性&quot;&gt;局限性&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;如果计算机上有多张网络适配器可以工作，但只有一个探测到连通状态，那么图标上依然会有一个感叹号，即探测为“网络受限”。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/59865134&quot;&gt;开机总是有一个弹窗 http://www.msftconnecttest.com/redirect ? - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.technet.microsoft.com/networking/2012/12/20/the-network-connection-status-icon/&quot;&gt;The Network Connection Status Icon - Networking Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.technet.microsoft.com/networking/2010/09/08/network-location-awareness-nla-and-how-it-relates-to-windows-firewall-profiles/&quot;&gt;Network Location Awareness (NLA) and how it relates to Windows Firewall Profiles - Networking Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.markwilson.co.uk/blog/2017/05/windows-network-connection-status-icon-ncsi.htm&quot;&gt;The Windows Network Connection Status Icon (NCSI) - markwilson.it&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://community.spiceworks.com/topic/1644424-what-must-be-unblocked-for-network-icon-to-not-say-no-internet-connection?page=1#entry-5876238&quot;&gt;What must be unblocked for network icon to not say “no internet connection?” - Windows 10 - Spiceworks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/aa370795%28v=vs.85%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;NLM_CONNECTIVITY enumeration (Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/en-us/windows/forum/windows_10-networking/msftconnecttestcom/54cd5060-dbd3-4c82-b958-1a8706184a88?auth=1&quot;&gt;msftconnecttest.com - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.technet.microsoft.com/Forums/windows/en-US/5a696f31-04a8-4852-8050-780208263a0c/httpwwwmsftconnecttestcomredirect?forum=win10itpronetworking&quot;&gt;http://www.msftconnecttest.com/redirect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc766017(v=ws.10)?wt.mc_id=MVP&quot;&gt;Appendix K: Network Connectivity Status Indicator and Resulting Internet Communication in Windows Vista - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/connection-status-icon.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/connection-status-icon.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何编写 WPF 的标记扩展 MarkupExtension，即便在 ControlTemplate/DataTemplate 中也能生效</title>
        <description>&lt;p&gt;WPF 的标记扩展为 WPF 带来了强大的扩展性。利用自定义的标记扩展，我们能够为 XAML 中的属性提供各种各样种类的值，而不仅限于自带的那一些。&lt;/p&gt;

&lt;p&gt;不过有小伙伴发现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt; 中编写标记扩展有时并不能正常工作，而本文将提供解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文并不会详细讲解如何编写 WPF 的标记扩展，如果你想了解相关的知识，建议阅读官网：&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/markup-extensions-and-wpf-xaml?wt.mc_id=MVP&quot;&gt;Markup Extensions and WPF XAML - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写简单的标记扩展&quot;&gt;编写简单的标记扩展&lt;/h2&gt;

&lt;p&gt;一个简单的标记扩展会是像这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RevealBorderBrushExtension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupExtension&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProvideValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样的标记扩展如此简单，以至于你可以在任意的 XAML 中用。只要赋值的那个属性接受 &lt;code class=&quot;highlighter-rouge&quot;&gt;Brush&lt;/code&gt; 类型，就不会出错。&lt;/p&gt;

&lt;p&gt;然而……有小伙伴写了更加复杂的标记扩展，在标记扩展中还通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;serviceProvider&lt;/code&gt; 拿到了目标控件的一些属性。本来一直好好工作的，结果有一天这个标记扩展被用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 上，然后就挂了……挂了……&lt;/p&gt;

&lt;h2 id=&quot;编写能在-controltemplate-中使用的标记扩展&quot;&gt;编写能在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 中使用的标记扩展&lt;/h2&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 中，XAML 标记扩展也是立即执行的，这就意味着当标记扩展中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProvideValue&lt;/code&gt; 执行时，还没有根据模板创建控件呢，那创建的是什么呢？&lt;/p&gt;

&lt;p&gt;是一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.SharedDp&lt;/code&gt; 的对象，不明白是什么？没关系，微软把这个类设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; 了，就是不想让你明白。所以，如果我们的标记扩展需要用到实际控件的一些功能（例如需要订阅事件、需要绑定、需要获取布局……），那么你就需要对 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.SharedDp&lt;/code&gt; 进行判断了。&lt;/p&gt;

&lt;p&gt;具体来说，是加上这样的判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharedDp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更完整一点写出来，就是这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RevealBorderBrushExtension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarkupExtension&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProvideValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果没有服务，则直接返回。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serviceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IProvideValueTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IProvideValueTarget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// MarkupExtension 在样式模板中，返回 this 以延迟提供值。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SharedDp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果不是 FrameworkElement，那么返回 this 以延迟提供值。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetObject&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 如果是设计时，那么返回白色&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesignerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetIsInDesignMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这一句是编译不通过的，我只是拿来做示范。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可能会觉得这段代码有些熟悉，如果有这种感觉，说明你可能阅读过我的另一篇博客：&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-markup-extension-in-control-template.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-markup-extension-in-control-template.html</guid>
        
        
        <category>xaml</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文</title>
        <description>&lt;p&gt;如果是在 GUI 中要求用户输入密码，各 UI 框架基本都提供了用于输入密码的控件；在这些控件中，用户在输入密码的时候会显示掩码。然而对于控制台程序来说，并没有用于输入密码的原生方法。&lt;/p&gt;

&lt;p&gt;本文将讲述一种在控制台中输入密码，并仅显示掩码的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;开始简单的程序&quot;&gt;开始简单的程序&lt;/h2&gt;

&lt;p&gt;让我们开始一个简单的 .NET Core 控制台程序。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;用户名: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;密  码: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-26-16-40-58.png&quot; alt=&quot;初步的程序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;密码直接显示，暴露无遗。而且，由于我们后面持续不断的有输出，控制台不会清除掉这些输出，所以密码会一直显示到缓冲区中——&lt;strong&gt;这显然是不能接受的&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;写一个让用户输入密码并显示掩码的方法&quot;&gt;写一个让用户输入密码并显示掩码的方法&lt;/h2&gt;

&lt;p&gt;既然控制台本身并没有提供可以为密码进行掩码的方法，那么我们只能自己来写了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecureString&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadPassword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SecureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConsoleKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Backspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\b \b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyChar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeReadOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;方法内部接受用户的输入——如果是回车，则确认；如果是退格，则删除一个字；其他情况下输出掩码。全程使用安全的字符串 &lt;code class=&quot;highlighter-rouge&quot;&gt;SecureString&lt;/code&gt;，这种字符串是没有办法直接通过托管代码获取值的。&lt;/p&gt;

&lt;p&gt;这时再输入字符串，将只能看到掩码——再也看不出来 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv 是不是一个逗比&lt;/code&gt; 了……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-26-16-49-03.png&quot; alt=&quot;有掩码的输入&quot; /&gt;&lt;/p&gt;

&lt;p&gt;需要注意的是，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;password&lt;/code&gt; 返回之前，我们调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SecureString.MakeReadOnly()&lt;/code&gt; 方法，将字符串设为只读，确保返回之后不会再被外面修改。&lt;/p&gt;

&lt;h2 id=&quot;转换密码&quot;&gt;转换密码&lt;/h2&gt;

&lt;p&gt;当然，只有对安全级别比较高的库才会接受 &lt;code class=&quot;highlighter-rouge&quot;&gt;SecureString&lt;/code&gt; 类型的字符串作为密码；一些简单的库只接受字符串类型的密码。那么在这些简单的库中我们如何才能得到普通的字符串呢？&lt;/p&gt;

&lt;p&gt;可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal&lt;/code&gt; 来完成：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecureString&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valuePtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valuePtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SecureStringToGlobalAllocUnicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStringUni&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valuePtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ZeroFreeGlobalAllocUnicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valuePtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以间接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;NetworkCredential&lt;/code&gt; 完成：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecureString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NetworkCredential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;NetworkCredential&lt;/code&gt; 的内部其实也是使用类似的方式获取到字符串的（详见 &lt;a href=&quot;https://referencesource.microsoft.com/#System/net/System/Net/UnsafeNativeMethods.cs,182c88988a485cda,references&quot;&gt;SecureStringHelper.CreateString - Reference Source&lt;/a&gt;）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecureString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plainString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bstr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bstr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SecureStringToBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secureString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;plainString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStringBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bstr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ZeroFreeBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plainString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/3404421/6233938&quot;&gt;c# - Password masking console application - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/818704/6233938&quot;&gt;c# - How to convert SecureString to System.String? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring.makereadonly?redirectedfrom=MSDN&amp;amp;view=netframework-4.7.2#System_Security_SecureString_MakeReadOnly?wt.mc_id=MVP&quot;&gt;SecureString.MakeReadOnly Method (System.Security) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#System/net/System/Net/UnsafeNativeMethods.cs,182c88988a485cda,references&quot;&gt;SecureStringHelper.CreateString - Reference Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/input-password-with-mask-in-cli.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/input-password-with-mask-in-cli.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target（附各种自带的 Task）</title>
        <description>&lt;p&gt;我之前写过一篇 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt;，其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点就是负责编译流程的最关键的节点。但因为篇幅限制，那篇文章不便详说。于是，我在本文说说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;target-的节点结构&quot;&gt;Target 的节点结构&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 内部几乎有着跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Project&amp;gt;&lt;/code&gt; 一样的节点结构，内部也可以放 &lt;code class=&quot;highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt;，不过还能放更加厉害的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。按照惯例，我依然用思维导图将节点结构进行了总结：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-20-16-34-13.png&quot; alt=&quot;Target 的节点结构&quot; /&gt;&lt;br /&gt;
▲ 上面有绿线和蓝线区分，仅仅是因为出现了交叉，怕出现理解歧义&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Hash&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;WriteCodeFragment&amp;gt;&lt;/code&gt; 都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;。我们可以看到，&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 是多种多样的，它可以占用一个 xml 节点。而本例中，&lt;code class=&quot;highlighter-rouge&quot;&gt;WriteCodeFragment&lt;/code&gt; Task 就是生成代码文件，并且将生成的文件作为一项 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 的 Item 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileWrites&lt;/code&gt; 的 Item。在 &lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程&lt;/a&gt; 中我们提到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemGroup&lt;/code&gt; 的节点，其作用由 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 指定。所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 会在名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 中使用，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileWrites&lt;/code&gt; 在 Microsoft.NET.Sdk 的多处都生成了这样的节点，不过目前从我查看到的全部 Microsoft.NET.Sdk 中，发现内部并没有使用它。&lt;/p&gt;

&lt;h2 id=&quot;target-执行的时机和先后顺序&quot;&gt;Target 执行的时机和先后顺序&lt;/h2&gt;

&lt;p&gt;既然 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 内部节点很大部分跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Project&amp;gt;&lt;/code&gt; 一样，那区别在哪里呢？&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Project&amp;gt;&lt;/code&gt; 里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt; 是静态的状态，如果使用 Visual Studio 打开项目，那么所有的状态将会直接在 Visual Studio 的项目文件列表和项目属性中显示；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 内部的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt; 是在编译期间动态生成的，不会在 Visual Studio 中显示；不过，它为我们提供了一种在编译期间动态生成文件或属性的能力。&lt;/p&gt;

&lt;p&gt;总结起来就是——&lt;strong&gt;Target 是在编译期间执行的&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;不过，同样是编译期间，那么多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，它们之间的执行时机是怎么确定的呢？&lt;/p&gt;

&lt;p&gt;一共有五个属性决定了 Target 之间的执行顺序：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Project 的属性
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InitialTargets&lt;/code&gt; 项目初始化的时候应该执行的 Target&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DefaultTargets&lt;/code&gt; 如果没有指定执行的 Target，那么这个属性将指定执行的 Target&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Target 的属性
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOnTargets&lt;/code&gt; 在执行此 Target 之前应该执行的另一个或多个 Target&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BeforeTargets&lt;/code&gt; 这是 MSBuild 4.0 新增的，指定应该在另一个或多个 Target 之前执行&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AfterTargets&lt;/code&gt; 这也是 MSBuild 4.0 新增的，指定应该在另一个或多个 Target 之后执行&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过指定这些属性，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 能够被 MSBuild 自动选择合适的顺序进行执行。例如，当我们希望自定义版本号，那么就需要赶在我们此前提到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenerateAssemblyInfo&lt;/code&gt; 之前执行。&lt;/p&gt;

&lt;h2 id=&quot;microsoftnetsdk-为我们提供的现成可用的-task&quot;&gt;Microsoft.NET.Sdk 为我们提供的现成可用的 Task&lt;/h2&gt;

&lt;p&gt;有 Microsoft.NET.Sdk 的帮助，我们可以很容易地编写自己的 Target，因为很多功能它都帮我们实现好了，我们排列组合一下就好。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Copy&lt;/code&gt; 复制文件 &lt;a href=&quot;https://lindexi.oschina.io/lindexi/post/Rosyln-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-MSBuild-Copy-%E5%A4%8D%E5%88%B6%E6%96%87%E4%BB%B6.html&quot;&gt;Rosyln 如何使用 MSBuild Copy 复制文件&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 移动文件 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/move-task?wt.mc_id=MVP&quot;&gt;Move Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Delete&lt;/code&gt; 删除文件&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 显示一个输出信息（我在 &lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包&lt;/a&gt; 中利用这个进行调试）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Warning&lt;/code&gt; 显示一个警告信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Error&lt;/code&gt; 报错（这样，编译就会以错误结束）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CombinePath&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ConvertToAbsolutePath&lt;/code&gt; 拼接路径，转成绝对路径&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CreateItem&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateProperty&lt;/code&gt; 创建项或者属性&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Csc&lt;/code&gt; 调用 csc.exe 编译 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/csc-task?wt.mc_id=MVP&quot;&gt;Csc Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MSBuild&lt;/code&gt; 编译一个项目 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task?wt.mc_id=MVP&quot;&gt;MSBuild Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exec&lt;/code&gt; 执行一个外部命令（我在 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt; 一文中利用到了这个 Task 执行命令）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WriteCodeFragment&lt;/code&gt; 生成一段代码 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/writecodefragment-task?wt.mc_id=MVP&quot;&gt;WriteCodeFragment Task&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WriteLinesToFile&lt;/code&gt; 向文件中写文字 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/writelinestofile-task?wt.mc_id=MVP&quot;&gt;WriteLinesToFile Task&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;提供的 Task 还有更多，如果上面不够你写出想要的功能，可以移步至官方文档翻阅：&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task-reference?wt.mc_id=MVP&quot;&gt;MSBuild Task Reference - Visual Studio - Microsoft Docs&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用自己写的-task&quot;&gt;使用自己写的 Task&lt;/h2&gt;

&lt;p&gt;我有另外的一篇文章来介绍&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;。如果希望自己写 Ta&lt;/p&gt;

&lt;h2 id=&quot;差量编译&quot;&gt;差量编译&lt;/h2&gt;

&lt;p&gt;如果你认为自己写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 执行比较耗时，那么就可以使用差量编译。我另写了一篇文章专门来说 Target 的差量编译：&lt;a href=&quot;/post/msbuild-incremental-build&quot;&gt;每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/target-build-order?wt.mc_id=MVP&quot;&gt;Target Build Order - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task-reference?wt.mc_id=MVP&quot;&gt;MSBuild Task Reference - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-msbuild-target.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-msbuild-target.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>C# 中那些可以被重载的运算符（操作符），以及使用它们的那些丧心病狂的语法糖</title>
        <description>&lt;p&gt;C# 中的运算符重载并不新鲜。然而，到底有哪些运算符可以重载，重载运算符可以用来做哪些丧心病狂的事情呢？&lt;/p&gt;

&lt;p&gt;本文收集了 C# 中所有可以重载的运算符，并且利用他们做了一些丧心病狂的语法糖。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;可以重载的运算符&quot;&gt;可以重载的运算符&lt;/h2&gt;

&lt;p&gt;运算符的重载比想象中的更加强大。因为——重载运算符时可以随意定义运算符中操作数的数据类型和返回值的类型。&lt;/p&gt;

&lt;p&gt;是的！&lt;strong&gt;不只是操作数，连返回值类型也能被重载&lt;/strong&gt;！&lt;/p&gt;

&lt;h3 id=&quot;一元运算符&quot;&gt;一元运算符&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;!&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;++&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;通过重载这些运算符，你可以改变某种类型操作后的返回类型和返回值。&lt;/p&gt;

&lt;p&gt;不过，等等！&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; 怎么会是一元运算符？不要忘了正数和负数哦！&lt;code class=&quot;highlighter-rouge&quot;&gt;+5&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;-6&lt;/code&gt; 这些其实是在使用一元运算符，而不是单纯的整数哦。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 也能被重载？是的，重载之后，你可以改变 &lt;code class=&quot;highlighter-rouge&quot;&gt;if(foo)&lt;/code&gt; 这样的判断的行为。参见：&lt;a href=&quot;https://blog.lindexi.com/post/C-%E5%BE%88%E5%B0%91%E4%BA%BA%E7%9F%A5%E9%81%93%E7%9A%84%E7%A7%91%E6%8A%80.html&quot;&gt;C# 很少人知道的科技&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;二元运算符&quot;&gt;二元运算符&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;%&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;^&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt; 运算符的重载是微软运算符重载部分的官方文档中并没有提及的。不过 Avalonia 项目利用这个不怎么常用的运算符做出了丧心病狂的绑定语法糖。参见 &lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Primitives/Popup.cs&quot;&gt;Avalonia/Popup.cs at master · AvaloniaUI/Avalonia&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_popupRoot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PopupRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChildProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MinWidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MinWidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxWidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxWidthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MinHeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MinHeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxHeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxHeightProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;必须成对重载的运算符&quot;&gt;必须成对重载的运算符&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;==&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;其实成对重载并不是什么很大的限制，大不了都写了就行。不过，重载它们依然能写出强大的语法糖代码来。&lt;/p&gt;

&lt;h3 id=&quot;只能被间接重载的运算符&quot;&gt;只能被间接重载的运算符&lt;/h3&gt;

&lt;h4 id=&quot;索引器显示转换或隐式转换&quot;&gt;索引器，显示转换或隐式转换&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[]&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;(T) x&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;前面 Avalonia 的绑定语法糖就充分利用了索引器的特点，使得能够在对象初始化器中初始化那些本没有直接定义在类型中的属性。&lt;/p&gt;

&lt;h4 id=&quot;赋值运算符&quot;&gt;赋值运算符&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;*=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;%=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;|=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;^=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&amp;gt;=&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这些运算符不可被重载。不过，其实它们都算作是原本的二元运算符与赋值操作的组合。所以，可以通过重载二元运算符来达到间接重载这些运算符。（当然，这样的方式，其赋值的作用是绝对丢不掉的）。&lt;/p&gt;

&lt;h2 id=&quot;逻辑运算符&quot;&gt;逻辑运算符&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;$$&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;||&lt;/code&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;可以阅读：[C# 重载条件逻辑运算符（&amp;amp;&amp;amp; 和&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;） - walterlv](/post/overload-conditional-and-and-or-operators-in-csharp)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;?:&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;通过重载 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 一元运算符可以达到目的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;??&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;可以阅读：&lt;a href=&quot;/post/overload-null-coalescing-operator-in-csharp&quot;&gt;C# 空合并运算符（??）不可重载？其实有黑科技可以间接重载！&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;不可被重载的运算符&quot;&gt;不可被重载的运算符&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;?:&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;??&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;=&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;as&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;checked&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;unchecked&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;delegate&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;sizeof&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;如果你还发现了其他黑科技来重载那些本不可以被重载的操作符，欢迎留言探讨。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators?wt.mc_id=MVP&quot;&gt;Overloadable Operators (C# Programming Guide) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/overridable-operators-in-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/overridable-operators-in-csharp.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何快速编写和调试 Emit 生成 IL 的代码</title>
        <description>&lt;p&gt;.NET Core/.NET Framework 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Reflection.Emit&lt;/code&gt; 命名空间为我们提供了动态生成 IL 代码的能力。利用这项能力，我们能够在运行时生成一段代码/一个方法/一个类/一个程序集。&lt;/p&gt;

&lt;p&gt;大家都知道反射的性能很差，通过缓存反射调用的方法则能够大幅提升性能。&lt;code class=&quot;highlighter-rouge&quot;&gt;Emit&lt;/code&gt; 为我们提供了这项能力，我们能够在运行时生成一段代码，替代使用反射动态调用的代码，以提升性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;我们在解决什么问题&quot;&gt;我们在解决什么问题？&lt;/h2&gt;

&lt;p&gt;之前我写过一篇&lt;a href=&quot;/post/create-delegate-to-improve-reflection-performance&quot;&gt;创建委托以大幅度提高反射调用的性能&lt;/a&gt;的方法，不过此方法适用于预先知道方法参数和返回值类型的情况。如果我们在编译期不知道类型，那么它就行不通了。（原因？注意到那篇文章中返回的委托有类型强转吗？也就是说需要编译期确定类型，即便是泛型。）&lt;/p&gt;

&lt;p&gt;例如，我们在运行时得到一个对象，希望为这个对象的部分或全部属性赋值；此对象的类型和属性类型在编译期全部不可知（就算是泛型也没有）。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefaultValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;众所周知的反射能够完成这个目标，但它不是本文讨论的重点；因为一旦这样的方法会被数万数十万甚至更多次调用的时候，反射将造成性能灾难。&lt;/p&gt;

&lt;p&gt;既然反射不行，通过反射的创建委托也不行，那还有什么方法？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用表达式树（不是本文重点）&lt;/li&gt;
  &lt;li&gt;使用 Emit（本文）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果事先不能知道类型，那么只能每次通过反射去动态的调用，于是才会耗费大量的性能。如果我们能够在运行时动态地生成一段调用方法，那么这个调用方法将可以缓存下来供后续重复调用。如果我们使用 Emit，那么生成的方法与静态编写的代码是一样的，于是就能获得普通方法的性能。&lt;/p&gt;

&lt;p&gt;为了实现动态地设置未知类型未知属性的值，我决定写出如下方法：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetPropertyValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;类的类型&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;属性名称&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;属性的类型&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;不用考虑编译问题了，这段代码是肯定编译不过的。方法是一个静态方法，传入两个参数——类型的实例和属性的新值；方法内部为实例中某个属性赋新值。&lt;/p&gt;

&lt;p&gt;类的类型、属性名称和属性的类型是编译期不能确定，但可以在运行时确定的；如果此生成的方法会被大量调用，那么性能优势将极其明显。&lt;/p&gt;

&lt;h2 id=&quot;快速编写-emit&quot;&gt;快速编写 Emit&lt;/h2&gt;

&lt;p&gt;为了快速编写和调试 Emit，我们需要 ReSharper 全家桶：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/resharper/&quot;&gt;ReSharper&lt;/a&gt; - &lt;em&gt;用于实时查看 IL 代码&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/decompiler/&quot;&gt;dotPeek&lt;/a&gt; - &lt;em&gt;免费，用于查看我们使用 Emit 生成的代码，便于对比分析&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;相比于原生 Visual Studio，有此工具帮助的情况下，IL 的编写速度和调试速度将得到质的提升。（当然，利用这些工具依然只是&lt;strong&gt;手工操作&lt;/strong&gt;，存在瓶颈；如果你阅读完本文之后找到或编写一个新的工具，更快，欢迎与我探讨。）&lt;/p&gt;

&lt;p&gt;ReSharper 提供了 IL Viewer 窗格，从菜单依次进入 ReSharper-&amp;gt;Windows-&amp;gt;IL Viewer 可以打开。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-22-18-35-42.png&quot; alt=&quot;ReSharper IL Viewer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开后立即可以看到我们当前正在编写的代码的 IL，而且还能高亮光标所在的代码块。（如果你的 IL Viewer 中没有代码或没有高亮，编译一遍项目即可。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-22-18-37-20.png&quot; alt=&quot;IL Viewer 中的代码与编写的代码对应&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们要做的，就是得知 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetPropertyValue&lt;/code&gt; 在编译后将得到什么样的 IL 代码，这样我们才能编写出正确的 IL 生成代码来。于是编写这些辅助代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TempClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SetPropertyValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetPropertyValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempClass&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TempProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译之后去 IL Viewer 中看 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetPropertyValue&lt;/code&gt; 的 IL 代码：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.method private hidebysig static void 
    SetPropertyValue(
        object this, 
        object 'value'
    ) cil managed 
{
    .maxstack 8

    // [14 9 - 14 10]
    IL_0000: nop          

    // [15 13 - 15 63]
    IL_0001: ldarg.0      // this
    IL_0002: castclass    Walterlv.Demo.TempClass
    IL_0007: ldarg.1      // 'value'
    IL_0008: castclass    [System.Runtime]System.String
    IL_000d: callvirt     instance void Walterlv.Demo.TempClass::set_TempProperty(string)
    IL_0012: nop          

    // [16 9 - 16 10]
    IL_0013: ret          

} // end of method Program::SetPropertyValue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将这段 IL 代码抄下来。怎么抄呢？看下面我抄的代码，你应该能够很容易看出里面一一对应的关系。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreatePropertySetter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaringType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 创建一个动态方法，参数依次为方法名、返回值类型、参数类型。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 对应着 IL 中的&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// .method private hidebysig static void&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     SetPropertyValue(&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ) cil managed&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DynamicMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;set_Property&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)});&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetILGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 定义形参。注意参数位置从 1 开始——即使现在在写静态方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 对应着 IL 中的&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     object this,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     object 'value'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;this&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 用 Emit 生成 IL 代码。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 对应着 IL 中的各种操作符。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ldarg_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Castclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ldarg_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 注意：下一句代码会在文章后面被修改。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Castclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Callvirt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 为生成的动态方法创建调用委托，返回返回这个委托。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在我们用下面新的代码替换之前写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 中直接赋值的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 测试代码。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TempClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 调用 Emit 核心代码。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QuickEmit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreatePropertySetter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 测试生成的核心代码能否正常工作。&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;直接运行，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;setValue&lt;/code&gt; 之后我们查看 &lt;code class=&quot;highlighter-rouge&quot;&gt;instance&lt;/code&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;TempProperty&lt;/code&gt; 属性的值，可以发现已经成功修改了。&lt;strong&gt;大功告成&lt;/strong&gt;！&lt;/p&gt;

&lt;h2 id=&quot;快速调试和修改-emit&quot;&gt;快速调试和修改 Emit&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;才没有大功告成呢&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;试试把 &lt;code class=&quot;highlighter-rouge&quot;&gt;TempProperty&lt;/code&gt; 的类型改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;。把测试代码中传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;test&quot;&lt;/code&gt; 字符串换成数字 &lt;code class=&quot;highlighter-rouge&quot;&gt;5&lt;/code&gt;。运行看看：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-22-19-37-33.png&quot; alt=&quot;VerificationException&quot; /&gt;&lt;br /&gt;
▲ 为什么会崩溃？！&lt;/p&gt;

&lt;p&gt;崩溃提示是“操作可能造成运行时的不稳定”。是什么造成了运行时的不稳定呢？难道是我们写的 IL 不对？&lt;/p&gt;

&lt;p&gt;现在开始&lt;strong&gt;利用 dotPeek 进行 IL 的调试&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;我们编写另外一个方法，用于将我们的生成的 IL 代码输出到 dll 文件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutputPropertySetter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaringType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 准备好要生成的程序集的信息。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Temp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineDynamicAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AssemblyBuilderAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineDynamicModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Temp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;set_Property&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;MethodAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CallingConventions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetILGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// 跟之前一样生成 IL 代码。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;this&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ldarg_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Castclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaringType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ldarg_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Castclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Callvirt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 将 IL 代码输出到程序的同级目录下。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样的，作为对照，我们在我们的测试程序中也修改那个参考代码。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetPropertyValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 注意！注意！string 已经换成了 int。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TempProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;重新生成可以得到一个 exe，调用新写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OutputPropertySetter&lt;/code&gt; 可以得到 Temp.dll。于是我们的输出目录下现在存在两个程序集：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-22-19-56-07.png&quot; alt=&quot;两个输出程序集&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将他们都拖进 dotPeek 中，然后在顶部菜单 Windows-&amp;gt;IL Viewer 中打开 IL 显示窗格。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-22-20-03-08.png&quot; alt=&quot;比较生成的 IL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;发现什么了吗？是的！对于结构体，用的是&lt;strong&gt;拆箱&lt;/strong&gt;！！！而不是强制类型转换。&lt;/p&gt;

&lt;p&gt;知道有了拆箱，于是就能知道应该怎样改了，生成 IL 的代码中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Castclass&lt;/code&gt; 部分应该根据条件进行判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;castingCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PropertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsValueType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unbox_Any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Castclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;castingCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在运行，即可正常通过。如果你希望拥有完整的代码，可以自行将以上两句替换掉此前注释说明了 &lt;code class=&quot;highlighter-rouge&quot;&gt;注意：下一句代码会在文章后面被修改。&lt;/code&gt; 的地方。&lt;/p&gt;

&lt;h2 id=&quot;更进一步&quot;&gt;更进一步&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;如果要 Emit 的代码中存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 这样的非顺序结构怎么办？&lt;em&gt;阅读 &lt;a href=&quot;/post/generate-il-using-emit&quot;&gt;使用 Emit 生成 IL 代码 - 吕毅&lt;/a&gt; 可以了解做法。&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;我们可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt; 类型的属性赋值，但在本例代码中却不可行，如何解决这种隐式转换的问题？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你尝试编写了 Emit 的代码，那么上面的问题应该难不倒你。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;通过 Emit，我们能够在运行时动态生成 IL 代码，以解决反射动态调用方法造成的大量性能损失。&lt;/li&gt;
  &lt;li&gt;通过 ReSharper 插件，我们可以实时查看生成的 IL 代码。&lt;/li&gt;
  &lt;li&gt;我们可以将 Emit 生成的代码输出到程序集文件。&lt;/li&gt;
  &lt;li&gt;通过 dotPeek，我们可以查看程序集中类型和方法的 IL 代码。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;生成方法签名与元数据
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.parameterbuilder(v=vs.110).aspx&quot;&gt;ParameterBuilder Class (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.defineparameter(v=vs.110).aspx&quot;&gt;MethodBuilder.DefineParameter Method (Int32, ParameterAttributes, String) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/9zksbcwc(v=vs.100).aspx&quot;&gt;Defining a Parameter with Reflection Emit&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/33656409/6233938&quot;&gt;c# - How to set “.maxstack” with ILGenerator - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;生成方法体
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.definelabel(v=vs.110).aspx&quot;&gt;ILGenerator.DefineLabel Method (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.marklabel(v=vs.110).aspx&quot;&gt;ILGenerator.MarkLabel Method (Label) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/15279066/6233938&quot;&gt;c# - Emit local variable and assign a value to it - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/11139241/6233938&quot;&gt;C# reflection: If … else? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.emit(v=vs.71).aspx&quot;&gt;ILGenerator.Emit Method&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.emit(v=vs.110).aspx&quot;&gt;ILGenerator.Emit Method (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/yf2s00wd(v=vs.110).aspx&quot;&gt;ILGenerator.Emit Method (OpCode, String) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/xz8067x2(v=vs.110).aspx&quot;&gt;ILGenerator.Emit Method (OpCode, MethodInfo) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.emitcall(v=vs.110).aspx&quot;&gt;ILGenerator.EmitCall Method (OpCode, MethodInfo, Type[]) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/193952/6233938&quot;&gt;.net - Call and Callvirt - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;IL 操作
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldarg_0(v=vs.110).aspx&quot;&gt;OpCodes.Ldarg_0 Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.brfalse_s(v=vs.110).aspx&quot;&gt;OpCodes.Brfalse_S Field (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;输出程序集
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/29037961/6233938&quot;&gt;c# - Is there a way to view the generated IL code of a DynamicMethod (in Sigil)? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/6501618/6233938&quot;&gt;c# - Can I use Reflection.Emit for generating code and save generated codes in .cs files or I could use CodeDom? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/8zwdfdeh(v=vs.110).aspx&quot;&gt;AssemblyBuilder.Save Method (String) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;运行时错误
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/16950272/6233938&quot;&gt;c# - Reflection.emit System.InvalidProgramException: Common Language Runtime detected an invalid program - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/jbevain/mono.reflection/issues/14&quot;&gt;“Operation could destabilize the runtime.” when using IL to create a DynamicMethod · Issue #14 · jbevain/mono.reflection&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/21496490/6233938&quot;&gt;c# - Emit Operation could destabilize the runtime for incrementing field - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://elegantcode.com/2012/08/23/net-4-5-operation-could-destabilize-the-runtime-yikes/&quot;&gt;.NET 4.5 : Operation could destabilize the runtime (yikes!) - ElegantCode&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/378895/6233938&quot;&gt;c# - Operation could destabilize the runtime? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;其他
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/generating-and-compiling-source-code-from-a-codedom-graph?wt.mc_id=MVP&quot;&gt;Generating and Compiling Source Code from a CodeDOM Graph - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-quickly-write-emit-code.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-quickly-write-emit-code.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>UWP 流畅设计中的光照效果（容易的 RevealBorderBrush 和不那么容易的 RevealBackgroundBrush）</title>
        <description>&lt;p&gt;在 Windows 10.0.16299 中，RevealBrush 被引入，可以实现炫酷的鼠标滑过高亮效果和点击光照。本文将告诉大家如何完整地实现这样的效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;reveal-的效果自带&quot;&gt;Reveal 的效果（自带）&lt;/h2&gt;

&lt;p&gt;在微软官方推荐的 &lt;a href=&quot;https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlUIBasics&quot;&gt;XAML Controls Gallery&lt;/a&gt; 应用中，我们可以找到 Reveal 的实现章节。下图是应用中演示的 Reveal 效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-15-reveal-effect-in-gallery.gif&quot; alt=&quot;Reveal in XAML Controls Gallery&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过在其实现中，全都是使用的系统自带的样式，例如：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource ButtonRevealStyle}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource CustomAcrylicInAppBrush_dark}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RequestedTheme=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Dark&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Orientation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vertical&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Orientation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource AppBarButtonRevealStyle}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1, 2, 0, 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource AppBarButtonRevealStyle}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CellPhone&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0, 2, 1, 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Orientation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource AppBarButtonRevealStyle}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Delete&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1, 2, 0, 2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource AppBarButtonRevealStyle}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Comment&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0, 2, 1, 2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;reveal-的制作自己实现&quot;&gt;Reveal 的制作（自己实现）&lt;/h2&gt;

&lt;p&gt;采用自带效果的控件看起来实现很容易，不过 UWP 控件的自带样式略坑，自己实现控件样式和模板是不可避免的事儿。&lt;/p&gt;

&lt;p&gt;这是定制的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListViewItem&lt;/code&gt; 的模板的一部分，写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBorderBrush&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBackgroundBrush&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 1 1 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.BorderBrush&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RevealBorderBrush&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.BorderBrush&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Background&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RevealBackgroundBrush&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Background&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行看，发现只有边框效果，背景效果是不存在的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-15-reveal-border-worked.gif&quot; alt=&quot;只有边框光照效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而官方文档对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBackgroundBrush&lt;/code&gt; 的实现竟然没有提及，也是挺奇怪的。比如：&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/style/reveal&quot;&gt;Reveal highlight - UWP app developer - Microsoft Docs&lt;/a&gt; 和 &lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.revealbackgroundbrush?wt.mc_id=MVP&quot;&gt;RevealBackgroundBrush Class (Windows.UI.Xaml.Media) - UWP app developer - Microsoft Docs&lt;/a&gt; 。&lt;/p&gt;

&lt;p&gt;注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBackgroundBrush&lt;/code&gt; 有一个附加属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBrush.State&lt;/code&gt;，设置到控件上用于指定采用哪一种光照效果：&lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBrush.State=&quot;Pressed&quot;&lt;/code&gt;。直接将其设置到控件上，发现依然是没有效果的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-15-reveal-border-worked.gif&quot; alt=&quot;依然没有效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看来需要动态地改变，于是必须加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualStateManager&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 1 1 0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.BorderBrush&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RevealBorderBrush&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.BorderBrush&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.Background&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RevealBackgroundBrush&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.Background&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommonStates&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Selected&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOver&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.(RevealBrush.State)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOver&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOverSelected&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.(RevealBrush.State)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOver&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PointerOverPressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.(RevealBrush.State)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.(RevealBrush.State)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PressedSelected&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.(RevealBrush.State)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pressed&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisabledStates&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Enabled&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Disabled&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Root.RevealBorderThickness&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在以上这段新的代码中，我们适时在指针设备滑过的时候切换 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBrush.State&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;PointerOver&lt;/code&gt;，在按下时切换 &lt;code class=&quot;highlighter-rouge&quot;&gt;RevealBrush.State&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Pressed&lt;/code&gt;。再次运行才发现背景光照效果正常出现了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-15-all-reveal-worked.gif&quot; alt=&quot;Reveal 背景光照效果出现&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;本文相关&quot;&gt;本文相关&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;本文所设计的源码来自我的一个个人兴趣项目，已在 GitHub 上开源：&lt;a href=&quot;https://github.com/walterlv/AssembleMailing&quot;&gt;walterlv/AssembleMailing&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;我写过另一篇让 WPF 实现光照效果的博客：&lt;a href=&quot;/post/fluent-design-reveal-brush-in-wpf&quot;&gt;流畅设计 Fluent Design System 中的光照效果 RevealBrush，WPF 也能模拟实现啦！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/uwp-reveal-background-brush.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/uwp-reveal-background-brush.html</guid>
        
        
        <category>uwp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>使用 MSBuild 响应文件 (rsp) 来指定 dotnet build 命令行编译时的大量参数</title>
        <description>&lt;p&gt;在为开源项目 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer/&quot;&gt;dotnet-campus/MSTestEnhancer&lt;/a&gt; 进行持续集成编译时，需要在编译命令中传入较多的参数。这对于新接手此项目的人来说，成本还是高了一点儿。本文将介绍 MSBuild 响应文件 (MSBuild Response Files, *.rsp) 来优化命令行编译体验。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我们在 msbuild 命令中加入 &lt;code class=&quot;highlighter-rouge&quot;&gt;/?&lt;/code&gt; 参数可以看到它对响应文件的解释：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 省略了一部分输出，只保留响应文件相关的两个。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&amp;lt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;从文本文件插入命令行设置。若要指定&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;多个响应文件，请分别指定每个响应&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;文件。&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;自动从以下位置使用任何名为&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;msbuild.rsp&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;响应文件&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;msbuild.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的目录&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;生成的第一个项目或解决方案的目录&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noautoresponse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;不自动包括任何&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MSBuild.rsp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;文件。&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;缩写&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/noautorsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet msbuild&lt;/code&gt; 或者直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild.exe&lt;/code&gt; 都是一样的具备此功能。&lt;/p&gt;

&lt;p&gt;那么响应文件到底是什么呢？我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令后传入的参数就可以是响应文件的内容。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;响应文件以 &lt;code class=&quot;highlighter-rouge&quot;&gt;.rsp&lt;/code&gt; 扩展名结尾，放在任何地方就行，只需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 命令中用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; 指定即可。&lt;/li&gt;
  &lt;li&gt;也可以用预定的文件名 &lt;code class=&quot;highlighter-rouge&quot;&gt;Directory.Build.rsp&lt;/code&gt;，放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;sln&lt;/code&gt; 同级目录或者父级目录中。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;比如，在这个项目中，我直接在解决方案同级目录中新建了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Directory.Build.rsp&lt;/code&gt; 文件，并写入这些内容：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;/p:Configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/maxcpucount&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/p:Version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6.0&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-beta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/p:AssemblyVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.6.0.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，当执行命令 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet msbuild&lt;/code&gt; 时，将执行这些事情：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使用 Release 配置进行编译&lt;/li&gt;
  &lt;li&gt;当前计算机有多少 CPU 核，就使用多少个进程进行并行编译&lt;/li&gt;
  &lt;li&gt;NuGet 包打包版本设置为 1.6.0-beta（这将覆盖 csproj 中设置的 Version 属性）&lt;/li&gt;
  &lt;li&gt;程序集版本设置为 1.6.0。0（这将覆盖 csproj 中设置的 AssemblyVersion 属性）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，还可以写更多的事情，但命令依旧简单——对新开发者是非常友好的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files?wt.mc_id=MVP&quot;&gt;MSBuild Response Files - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/20414366/6233938&quot;&gt;Default or specify msbuild properties in an external file - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://superuser.com/questions/764631/how-to-use-msbuild-rsp-or-otherwise-specify-default-visual-studio-msbuild-exe-co&quot;&gt;How to use MSBuild.rsp or otherwise specify default Visual Studio MSBuild.exe command line switches - Super User&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-response-files.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-response-files.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>Roslyn 的确定性构建</title>
        <description>&lt;p&gt;注意到每次编译完之后，你的 dll 或者 exe 是不一样的吗？本来这并没有什么大不了的，但大家都知道数字和鹅厂的安全软件遍布在我们大(tiān)陆(cháo)地区的大量电脑上，它们的查杀策略是——凡是不认识的一律是病毒木马；于是每次不一样的编译很容易引起它们的警告——真不想每次都把编译后的样本提交给它们存档入库。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;确定性编译&quot;&gt;确定性编译&lt;/h2&gt;

&lt;p&gt;于是有一天意外地发现了 Roslyn 的确定性构建。&lt;/p&gt;

&lt;p&gt;方法是在 csproj 文件中加入 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Deterministic/&amp;gt;&lt;/code&gt; 标记。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;Deterministic&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Deterministic&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后重新生成 dll 或 exe，多生成几次（每次都重新生成），会发现每次验证文件的 Hash 值都是一样的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-31-16-33-42.png&quot; alt=&quot;Deterministic Hash&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，一旦我们去掉这个标记，再验证 Hash 值，就开始改变了，而且每次都不一样。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-31-16-35-34.png&quot; alt=&quot;Non-deterministic Hash&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;不确定的编译&quot;&gt;不确定的编译&lt;/h2&gt;

&lt;p&gt;是什么导致了没有加此标记时每次编译都不一样呢？最少有三个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;MVID&lt;/strong&gt;：当初微软在制定 CLI 标准时就说每次编译都应该在 PE 头生成新的 Id（很多工具都直接使用了 guid）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PDB ID&lt;/strong&gt;：一个跟新生成的 PDB 文件匹配的 GUID 标识符&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;时间戳&lt;/strong&gt;：每次编译都要把当前时间加上&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，如果你的版本号使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.0.*&lt;/code&gt; 这样的动态版本号，那么每次编译还会新增一个构建号。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?wt.mc_id=MVP&quot;&gt;Customize your build - Visual Studio - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/blob/master/docs/compilers/Deterministic%20Inputs.md&quot;&gt;roslyn/Deterministic Inputs.md at master · dotnet/roslyn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/aelij/b20271f4bd0ab1298e49068b388b54ae&quot;&gt;Deterministic Builds in C#&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/issues/372&quot;&gt;[Umbrella] Compilers should be deterministic: same inputs generate same outputs · Issue #372 · dotnet/roslyn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html&quot;&gt;Deterministic builds in Roslyn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deterministic-builds-in-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deterministic-builds-in-roslyn.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>roslyn</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>（持续整理中）Visual Studio 中 C# 代码分析规则集中每一项的含义 (stylecop ruleset)</title>
        <description>&lt;p&gt;因为我希望在要求很高的库中及时发现潜在的代码问题，所以我开启了 Visual Studio 的代码分析。&lt;/p&gt;

&lt;p&gt;但是在修改规则的时候发现规则的名称都是在用&lt;strong&gt;我懂的每一个字描述我一点都不懂的概念&lt;/strong&gt;，于是打算一个个尝试以找出每一个代码分析的实际意义。&lt;/p&gt;

&lt;p&gt;在整理的过程当中，发现要么是名称看不懂，要么是错误提示看不懂。不过两个合在一起并配合代码实验之后，基本上都能够看懂了。于是，把已经整理的部分都分享出来。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;正在整理中……&lt;/p&gt;

&lt;h2 id=&quot;代码分析microsoftanalyzersmanagedcodeanalysis&quot;&gt;代码分析（Microsoft.Analyzers.ManagedCodeAnalysis）&lt;/h2&gt;

&lt;h3 id=&quot;设计问题&quot;&gt;设计问题&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;编号&lt;/th&gt;
      &lt;th&gt;名称&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1004&lt;/td&gt;
      &lt;td&gt;泛型方法应提供类型参数&lt;/td&gt;
      &lt;td&gt;如果泛型方法的参数列表中没有用到声明的所有泛型，那么就会出现此提示（这是因为此时泛型不能被隐式推断，库使用者的学习成本会提高，详见：&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1004-generic-methods-should-provide-type-parameter?wt.mc_id=MVP&quot;&gt;CA1004&lt;/a&gt;）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1005&lt;/td&gt;
      &lt;td&gt;避免泛型类型的参数过多&lt;/td&gt;
      &lt;td&gt;如果写泛型的时候有超过 2 个泛型类型，就会出现此提示&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1006&lt;/td&gt;
      &lt;td&gt;不要将泛型类型嵌套在成员签名中&lt;/td&gt;
      &lt;td&gt;如果出现类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;Func&amp;lt;Task&amp;lt;T&amp;gt;&lt;/code&gt; 这样的嵌套泛型出现在方法参数签名中，则会出现此提示&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1018&lt;/td&gt;
      &lt;td&gt;用 AttributeUsageAttribute 标记特性&lt;/td&gt;
      &lt;td&gt;如果继承自一个已有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，即便基类已经写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;AttributeUsage&lt;/code&gt;，此类型也应该再写一遍，以提高代码可读性和便于文档制作&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1019&lt;/td&gt;
      &lt;td&gt;定义特性参数的访问器&lt;/td&gt;
      &lt;td&gt;自定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 构造函数中的参数应该有一个能够访问此参数的只读属性&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1026&lt;/td&gt;
      &lt;td&gt;不应使用默认形参&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;void Method(object p = null)&lt;/code&gt; 这样的方法不兼容 CLS，于是不被推荐&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1033&lt;/td&gt;
      &lt;td&gt;接口方法应可由子类型调用&lt;/td&gt;
      &lt;td&gt;基类中显式实现了一个接口方法，导致子类中无法调用此接口方法&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1040&lt;/td&gt;
      &lt;td&gt;避免使用空接口&lt;/td&gt;
      &lt;td&gt;意思就是“避免使用空接口”，这种接口就像是一个标记一样并没有什么作用，考虑使用自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 来实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1045&lt;/td&gt;
      &lt;td&gt;不要通过引用来传递类型&lt;/td&gt;
      &lt;td&gt;方法参数中应该尽量避免使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ref&lt;/code&gt; 参数&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;全球化与本地化问题&quot;&gt;全球化与本地化问题&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;编号&lt;/th&gt;
      &lt;th&gt;名称&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1305&lt;/td&gt;
      &lt;td&gt;指定 IFormatProvider&lt;/td&gt;
      &lt;td&gt;如果格式化字符串（&lt;code class=&quot;highlighter-rouge&quot;&gt;string.Format&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;$&quot;&quot;&lt;/code&gt;），应该指定区域相关的属性，，否则容易出现本地化问题&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1307&lt;/td&gt;
      &lt;td&gt;指定 StringComparison&lt;/td&gt;
      &lt;td&gt;如果进行字符串比较或排序（&lt;code class=&quot;highlighter-rouge&quot;&gt;EndsWith&lt;/code&gt; 等），应该指定区域相关的属性，否则容易出现本地化问题&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1309&lt;/td&gt;
      &lt;td&gt;使用按顺序的 StringComparison&lt;/td&gt;
      &lt;td&gt;如果进行字符串比较或排序（&lt;code class=&quot;highlighter-rouge&quot;&gt;EndsWith&lt;/code&gt; 等），若要指定非语义比较，应该指定排序规则为 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;命名问题&quot;&gt;命名问题&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;编号&lt;/th&gt;
      &lt;th&gt;名称&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1704&lt;/td&gt;
      &lt;td&gt;标识符应正确拼写&lt;/td&gt;
      &lt;td&gt;如果命名成一些简单无意义的字符（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt;），那么会出现此提示&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1725&lt;/td&gt;
      &lt;td&gt;参数名应与基方法中声明保持一致&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;性能问题&quot;&gt;性能问题&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;编号&lt;/th&gt;
      &lt;th&gt;名称&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1800&lt;/td&gt;
      &lt;td&gt;避免进行不必要的强制转换&lt;/td&gt;
      &lt;td&gt;如果多次对同一个引用进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;as&lt;/code&gt;，则会出现此提示，应该仅转换一次，例如使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;value is var xxx&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1801&lt;/td&gt;
      &lt;td&gt;检查未使用的参数&lt;/td&gt;
      &lt;td&gt;如果方法中有声明的参数没有使用，则会发出此警告&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1813&lt;/td&gt;
      &lt;td&gt;避免使用未密封的特性&lt;/td&gt;
      &lt;td&gt;自定义的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 应该是 &lt;code class=&quot;highlighter-rouge&quot;&gt;sealed&lt;/code&gt; 的&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1822&lt;/td&gt;
      &lt;td&gt;将成员标记为 static&lt;/td&gt;
      &lt;td&gt;如果方法的实现中没有任何一个地方用到了 this，那么这个方法就应该标记成静态的&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CA1824&lt;/td&gt;
      &lt;td&gt;用 NeutralResourcesLanguage 标记程序集&lt;/td&gt;
      &lt;td&gt;如果程序集中包含资源，那么应该用此特性标记程序集以便提升第一次查找资源时的性能；&lt;code class=&quot;highlighter-rouge&quot;&gt;[assembly: NeutralResourcesLanguage(&quot;zh-CHS&quot;, UltimateResourceFallbackLocation.Satellite)]&lt;/code&gt; 表示如果当前系统处于简体中文环境，那么就去此程序集查找资源，否则就去附属程序集查找；如果资源一定在此程序集，则指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainAssembly&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;代码质量问题&quot;&gt;代码质量问题&lt;/h3&gt;

&lt;p&gt;这部分的代码分析来自于 &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers/&quot;&gt;Microsoft.CodeAnalysis.FxCopAnalyzers&lt;/a&gt;，安装此 NuGet 包后将获得更多的代码分析。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;编号&lt;/th&gt;
      &lt;th&gt;名称&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CA2007&lt;/td&gt;
      &lt;td&gt;不应该直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 一个而不调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConfigureAwait&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;建议阅读 &lt;a href=&quot;/post/using-configure-await-to-avoid-deadlocks&quot;&gt;在编写异步方法时，使用 ConfigureAwait(false) 避免使用者死锁&lt;/a&gt; 了解这样提示的原因&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/code-quality/code-analysis-for-managed-code-warnings?wt.mc_id=MVP&quot;&gt;Code Analysis for Managed Code Warnings - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/?wt.mc_id=MVP&quot;&gt;C# Compiler Errors - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/meaning-of-all-kind-of-stylecop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/meaning-of-all-kind-of-stylecop.html</guid>
        
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>生成代码，从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型</title>
        <description>&lt;p&gt;当你想写一个泛型 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;T&amp;gt;&lt;/code&gt; 的类型的时候，是否想过两个泛型参数、三个泛型参数、四个泛型参数或更多泛型参数的版本如何编写呢？是一个个编写？类小还好，类大了就杯具！&lt;/p&gt;

&lt;p&gt;事实上，在 Visual Studio 中生成代码的手段很多，本文采用最笨的方式生成，但效果也很明显——代码写得轻松写得爽！&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;我们想要的效果&quot;&gt;我们想要的效果&lt;/h2&gt;

&lt;p&gt;我们现在有一个泛型的版本：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 做某些事情。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 做其他事情。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;希望生成多个泛型的版本：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 做某些事情。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 做其他事情。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到类型的泛型变成了多个，参数从一个变成了多个，返回值从单个值变成了元组。&lt;/p&gt;

&lt;p&gt;于是，怎么生成呢？&lt;/p&gt;

&lt;h2 id=&quot;回顾-visual-studio-那些生成代码的方式&quot;&gt;回顾 Visual Studio 那些生成代码的方式&lt;/h2&gt;

&lt;p&gt;Visual Studio 原生自带两种代码生成方式。&lt;/p&gt;

&lt;h3 id=&quot;第一种t4-文本模板&quot;&gt;第一种：T4 文本模板&lt;/h3&gt;

&lt;p&gt;事实上 T4 模板算是 Visual Studio 最推荐的方式了，因为你只需要编写一个包含占位符的模板文件，Visual Studio 就会自动为你填充那些占位符。&lt;/p&gt;

&lt;p&gt;那么 Visual Studio 用什么填充？是的，可以在模板文件中写 C# 代码！比如官方 DEMO：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;#@ output extension=&quot;.txt&quot; #&amp;gt;  
&amp;lt;#@ assembly name=&quot;System.Xml&quot; #&amp;gt;  
&amp;lt;#  
 System.Xml.XmlDocument configurationData = ...; // Read a data file here.  
#&amp;gt;  
namespace Fabrikam.&amp;lt;#= configurationData.SelectSingleNode(&quot;jobName&quot;).Value #&amp;gt;  
{  
  ... // More code here.   
}  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;这代码写哪儿呢？在项目上右键新建项，然后选择“运行时文本模板”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-31-13-08-09.png&quot; alt=&quot;文本模板&quot; /&gt;&lt;/p&gt;

&lt;p&gt;T4 模板编辑后一旦保存（Ctrl+S），代码立刻生成。&lt;/p&gt;

&lt;p&gt;有没有觉得这代码着色很恐怖？呃……根本就没有代码着色好吗！即便如此，T4 本身也是非常强悍的代码生成方式。&lt;/p&gt;

&lt;p&gt;这不是本文的重点，于是感兴趣请阅读官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?wt.mc_id=MVP&quot;&gt;Code Generation and T4 Text Templates - Microsoft Docs&lt;/a&gt; 学习。&lt;/p&gt;

&lt;h3 id=&quot;第二种文件属性中的自定义工具&quot;&gt;第二种：文件属性中的自定义工具&lt;/h3&gt;

&lt;p&gt;右键选择项目中的一个代码文件，然后选择“属性”，你将看到以下内容：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-31-13-11-52.png&quot; alt=&quot;属性 - 自定义工具&quot; /&gt;&lt;/p&gt;

&lt;p&gt;就是这里的自定义工具。在这里填写工具的 Key，那么一旦这个文件保存，就会运行自定义工具生成代码。&lt;/p&gt;

&lt;p&gt;那么 Key 从哪里来？这货居然是从注册表拿的！也就是说，如果要在团队使用，还需要写一个注册表项！即便如此，自定义工具本身也是非常强悍的代码生成方式。&lt;/p&gt;

&lt;p&gt;这也不是本文的重点，于是感兴趣请阅读官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/custom-tools?wt.mc_id=MVP&quot;&gt;Custom Tools - Microsoft Docs&lt;/a&gt; 学习。&lt;/p&gt;

&lt;h3 id=&quot;第三种笨笨的编译生成事件&quot;&gt;第三种：笨笨的编译生成事件&lt;/h3&gt;

&lt;p&gt;这算是通常项目用得最多的方式了，因为它可以在不修改用户开发环境的情况下执行几乎任何任务。&lt;/p&gt;

&lt;p&gt;右键项目，选择属性，进入“生成事件”标签：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-31-13-15-55.png&quot; alt=&quot;项目生成事件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在“预先生成事件命令行”中填入工具的名字和参数，便可以生成代码。&lt;/p&gt;

&lt;h2 id=&quot;制作生成泛型代码的工具&quot;&gt;制作生成泛型代码的工具&lt;/h2&gt;

&lt;p&gt;我们新建一个控制台项目，取名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CodeGenerator&lt;/code&gt;，然后把我写好的生成代码粘贴到新的类文件中。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.BuildTools&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GenericTypeGenerator&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedHeader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;$@&quot;//------------------------------------------------------------------------------&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;// &amp;lt;auto-generated&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//     此代码由工具生成。&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//     运行时版本:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//     对此文件的更改可能会导致不正确的行为，并且如果&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//     重新生成代码，这些更改将会丢失。&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;// &amp;lt;/auto-generated&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;#define GENERATED_CODE&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedFooter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;$@&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_genericTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenericTypeGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_toolName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_genericTemplate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericTemplate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toolVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;[System.CodeDom.Compiler.GeneratedCode(\&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\&quot;, \&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\&quot;)]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_genericTemplate&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 替换泛型。&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;out T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;{0}&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;out T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Task&amp;lt;T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Task&amp;lt;({0})&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Func&amp;lt;T, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Func&amp;lt;{0}, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; T, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; {0}, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T, bool&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0}, bool&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var (t, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var ({0}, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, {0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;return (t, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;return ({0}, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;{0}&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T value)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(({0}) value)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n} t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var t =&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var ({0}) =&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; T &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; ({0}) &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; t;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; ({0});&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 生成 [GeneratedCode]。&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public interface &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public interface &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public sealed class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public sealed class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedHeader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedFooter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个类中加入了非常多种常见的泛型字符串特征，当然是采用最笨的字符串替换方法。如果感兴趣优化优化，可以用正则表达式，或者使用 Roslyn 扩展直接拿语法树。&lt;/p&gt;

&lt;p&gt;于是，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; 中调用以上代码即可完成泛型生成。我写了一个简单的版本，可以将每一个命令行参数解析为一个需要进行转换的泛型类文件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.BuildTools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argument&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;GenerateGenericTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateGenericTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 读取原始文件并创建泛型代码生成器。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenericTypeGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 根据泛型个数生成目标文件路径和文件内容。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetIndexedFileNameFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetFileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 写入目标文件。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetFileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetIndexedFileNameFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFileNameWithoutExtension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{0}.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;考虑到这是 Demo 级别的代码，我将生成的泛型个数直接写到了代码当中。这段代码的意思是按文件名递增生成多个泛型类。&lt;/p&gt;

&lt;p&gt;例如，有一个泛型类文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo.cs&lt;/code&gt;，则会在同目录生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo2.cs&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Demo3.cs&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Demo4.cs&lt;/code&gt;。当然，&lt;code class=&quot;highlighter-rouge&quot;&gt;Demo.cs&lt;/code&gt; 命名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo1.cs&lt;/code&gt; 结果也是一样的。&lt;/p&gt;

&lt;p&gt;在要生成代码的项目中添加“预先生成事件命令行”：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;$(ProjectDir)..\CodeGenerator\$(OutDir)net47\CodeGenerator.exe&quot; &quot;$(ProjectDir)..\Walterlv.Demo\Generic\IDemoFile.cs&quot; &quot;$(ProjectDir)..\..\Walterlv.Demo\Generic\DemoFile.cs&quot; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，编译此项目，即可生成多个泛型类了。&lt;/p&gt;

&lt;h2 id=&quot;彩蛋&quot;&gt;彩蛋&lt;/h2&gt;

&lt;p&gt;如果你仔细阅读了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GenericTypeGenerator&lt;/code&gt; 类的代码，你将注意到我为生成的文件加上了条件编译符“&lt;code class=&quot;highlighter-rouge&quot;&gt;GENERATED_CODE&lt;/code&gt;”。这样，你便可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;#ifdef GENERATED_CODE&lt;/code&gt; 来处理部分不需要进行转换或转换有差异的代码了。&lt;/p&gt;

&lt;p&gt;这时写代码，是不是完全感受不到正在写模板呢？既有代码着色，又适用于团队其他开发者的开发环境。是的，个人认为如果带来便捷的同时注意不到工具的存在，那么这个工具便是好的。&lt;/p&gt;

&lt;p&gt;如果将传参改为自动寻找代码文件，将此工具发布到 NuGet，那么可以通过 NuGet 安装脚本将以上过程全自动化完成。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?wt.mc_id=MVP&quot;&gt;Code Generation and T4 Text Templates - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/custom-tools?wt.mc_id=MVP&quot;&gt;Custom Tools - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/generate-code-of-generic-types.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/generate-code-of-generic-types.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>.NET Core 和 .NET Framework 中的 MEF2</title>
        <description>&lt;p&gt;MEF，Managed Extensibility Framework，现在已经发布了三个版本了，它们是 MEF 和 MEF2。&lt;/p&gt;

&lt;p&gt;等等！3 去哪儿了？本文将教大家完成基于 MEF2 的开发。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;mef-和-mef2&quot;&gt;MEF 和 MEF2&lt;/h2&gt;

&lt;p&gt;其实微软发布了四个版本的 MEF：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;随着 .NET Framework 4.0 发布，微软称之为 MEF&lt;/li&gt;
  &lt;li&gt;随着 .NET Framework 4.5 发布，微软让它更好用了，微软称之为 MEF2，但因为接口兼容，也直接称之为 MEF&lt;/li&gt;
  &lt;li&gt;.NET 开发团队觉得 MEF 第一代性能太差，于是通过 NuGet 为移动设备发布了可移植类库，是个轻量级版本，只移植了 .NET Framework 中 MEF2 里 2 的部分；随后 .NET Core 中也加入了 MEF2，也是 .NET Framework 中 MEF2 里 2 的部分&lt;/li&gt;
  &lt;li&gt;Visual Studio 开发团队觉得 .NET Framework 里的 MEF2 性能太差，NuGet 版的 MEF2 功能太少，于是自己又写了一个，微软称之为 VS-MEF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于第一代的 MEF，我们这里就完全不说了，性能又差功能又少，没有利用价值。&lt;/p&gt;

&lt;p&gt;对于 .NET Framework 4.5 里引入的 MEF2，性能上没能改进多少，倒是使用起来功能更多。详细资料和使用方法请参考微软官方的文档：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/mef/?wt.mc_id=MVP&quot;&gt;Managed Extensibility Framework (MEF) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/mef/attributed-programming-model-overview-mef?wt.mc_id=MVP&quot;&gt;Attributed Programming Model Overview (MEF) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而本文主要说的 MEF2 是微软后来以 NuGet 包形式发布的 MEF2；适用于 .NET Framework 4.5 及以上、.NET Core 和各种 .NET 移动平台。它的接口相比于 .NET Framework 中原生带的已经变了，中文和英文的参考资料很少，几乎都是参考微软官方发布的文档才能使用。所以本文将为大家提供其中文的使用方法指导。&lt;/p&gt;

&lt;p&gt;至于性能提升程度，我没有进行定量测试，所以直接从 &lt;a href=&quot;http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison&quot;&gt;IoC Container Benchmark - Performance comparison - www.palmmedia.de&lt;/a&gt; 一文中搬运了性能测试结果，如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-17-08-49-22.png&quot; alt=&quot;性能报告&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装-mef2&quot;&gt;安装 MEF2&lt;/h2&gt;

&lt;p&gt;.NET Framework 中自带的 MEF 在程序集 System.ComponentModel.Composition.dll 中，命名空间为 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.ComponentModel.Composition&lt;/code&gt;。MEF2 随 NuGet 包发布，其 NuGet 包名是 Microsoft.Composition，命名空间为 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Composition&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-17-23-10-07.png&quot; alt=&quot;Microsoft.Composition&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以，在需要使用 MEF2 的项目中安装以上 NuGet 包即可完成安装。&lt;/p&gt;

&lt;h2 id=&quot;使用-mef2-开发&quot;&gt;使用 MEF2 开发&lt;/h2&gt;

&lt;p&gt;MEF 完全使用特性来管理容器中的依赖，微软称之为 Attributed Programming Model，并辅以广告——不需要配置文件的依赖注入容器。所以，使用特性来标记依赖关系就成了 MEF 的招牌依赖管理方式。&lt;/p&gt;

&lt;p&gt;使用方法我将分为两个部分来讲，最容易的是业务代码，给开发团队中所有成员使用的代码。比较难的是框架代码，给开发团队中写框架的那一部分成员。&lt;/p&gt;

&lt;h3 id=&quot;业务代码&quot;&gt;业务代码&lt;/h3&gt;

&lt;p&gt;业务代码的写法其实取决于框架开发者怎么去定义框架。但是，为了方便大家理解，在这一节我将只说 MEF2 最原生的使用方法。框架那一节我才会说明如何自定义业务代码的写法。&lt;/p&gt;

&lt;p&gt;最原生的使用方法其实只有两个——&lt;code class=&quot;highlighter-rouge&quot;&gt;[Import]&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Export]&lt;/code&gt;，其它都是变种！具体说来，标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Export&lt;/code&gt; 的类将导出给其它类使用；标记了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Import&lt;/code&gt; 的属性/字段/方法参数等将接收来自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Export&lt;/code&gt; 的那些类/属性/字段的实例。&lt;/p&gt;

&lt;h4 id=&quot;importexport&quot;&gt;Import/Export&lt;/h4&gt;

&lt;p&gt;在类型上标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Export]&lt;/code&gt; 可以让容器发现这个类型。&lt;code class=&quot;highlighter-rouge&quot;&gt;[Export]&lt;/code&gt; 允许带两个参数，一个契约名称，一个契约类型。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Import]&lt;/code&gt; 的时候，相同的契约名称会被注入；与属性或字段的类型相同的契约类型会被注入。&lt;/p&gt;

&lt;h4 id=&quot;ienumerablelazy&quot;&gt;IEnumerable/Lazy&lt;/h4&gt;

&lt;p&gt;如果属性或字段是集合类型，可以使用 [ImportMany] 来注入集合（如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Export&lt;/code&gt; 有多个）。&lt;/p&gt;

&lt;p&gt;如果属性或字段是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; 类型，那么并不会立即注入，而是在访问到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy&amp;lt;T&amp;gt;.Value&lt;/code&gt; 时才获取到实例（如果此时的创建过程由容器处理，那么第一次访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;Value&lt;/code&gt; 时才会创建）。&lt;/p&gt;

&lt;h3 id=&quot;框架代码&quot;&gt;框架代码&lt;/h3&gt;

&lt;p&gt;框架代码也分为两个部分：一个部分是初始化，初始化后可以创建一个依赖注入容器；另一个部分是管理依赖，将使用之前初始化好的依赖注入容器进行管理。&lt;/p&gt;

&lt;p&gt;初始化的最简代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionHost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainerConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，得到的 compositionHost 变量将是用来管理依赖的容器，你可以将它储存在字段中用于随后管理依赖。&lt;/p&gt;

&lt;p&gt;但是，只是这么初始化将得不到任何对象。所以，我们需要额外添加配置代码，以便将一些程序集中的对象添加到容器中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionHost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainerConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAssemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，A/B/C/D 这四个类分别所在的程序集中，直接或间接加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Export]&lt;/code&gt; 特性的类都将被此依赖容器管理。&lt;/p&gt;

&lt;p&gt;MEF2 之所以为 2，因为它除了能通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Export]&lt;/code&gt; 特性导出，还能直接在框架中发现而不必由业务开发者手动指定。这在第三方代码或者不希望被 MEF 侵入的代码中非常有用。例如，我们将所有已有的 ViewModel 导出：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 使用 ConventionBuilder 自动导出所有的 ViewModel。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convention&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConventionBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 将所有继承自 ViewModelBase 的类导出，并共享一个实例（即注入到多个属性中的都是同一个实例）。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;convention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForTypesDerivedFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ViewModelBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 使用这些配置创建依赖注入容器。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionHost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainerConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAssemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDefaultConventions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，以上代码中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.Shared()&lt;/code&gt; 目的是让导出的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 共享实例（同一个类型的实例只有一个）。&lt;/p&gt;

&lt;p&gt;只初始化是不行的，还需要将这些依赖注入到目标实例中才行。使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;SatisfyImports&lt;/code&gt; 可以将传入的对象中的所有依赖注入进去。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;compositionHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SatisfyImports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在框架设计中，对于不同模块中的类型，框架需要决定使用哪一个容器来注入，或者是否注入。所以上面这个代码会发生在使用 MEF2 框架中需要注入的任何一个部分。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Tips/488513/MEF-in-NET&quot;&gt;MEF in .NET 4.5 - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/wintersun/archive/2013/01/16/2863405.html&quot;&gt;Managed Extensibility Framework(MEF) 2 框架新特性介绍 - PetterLiu - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/33522249/6233938&quot;&gt;Is MEF or MEF2 baked into the .NET Framework? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/vs-mef/blob/master/doc/why.md&quot;&gt;vs-mef/why.md at master · Microsoft/vs-mef&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MicrosoftArchive/mef/blob/master/Wiki/Home.md&quot;&gt;mef/Home.md at master · MicrosoftArchive/mef&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/mef2-from-nuget.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/mef2-from-nuget.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>C#/.NET 匿名函数会捕获变量，并延长对象的生命周期</title>
        <description>&lt;p&gt;小伙伴在一次垃圾回收中，发现对象并没有被回收掉，而注释掉一句代码后它便能够回收。&lt;/p&gt;

&lt;p&gt;这究竟是为什么？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;不关心探索过程的就直接拉到最后看结论吧！&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;探索&quot;&gt;探索&lt;/h2&gt;

&lt;p&gt;测试代码是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAnotherThing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要验证的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 对象是否被回收。然而在这段代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 并没有被回收；然而去掉最后一行，&lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 便可以正常回收。&lt;strong&gt;关键是，即便是在 Console.WriteLine 上打下断点，让代码永远不会执行到最后一句，也不会改变回收的结果。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomething&lt;/code&gt; 中的委托参数恰好就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 类型的，不禁让人觉得可能是此函数做了一些奇怪的事情。然而毕竟参数中传入的委托参数只是形参，理论上不应该影响到外部对象的回收。那么影响的只可能是变量的捕获了。&lt;/p&gt;

&lt;p&gt;于是，我们将最后一行换成别的函数别的参数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者将整个这一句提取成新的函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略前面的代码。&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExtractedMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExtractedMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoAnotherThing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，回收就会正常进行。&lt;/p&gt;

&lt;p&gt;现在，不执行这个&lt;em&gt;受争议的&lt;/em&gt;函数了，我们使用空的匿名函数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一样会导致不回收。&lt;/p&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;

&lt;p&gt;在微软官方的《C# 规范 5.0》（&lt;a href=&quot;http://www.microsoft.com/en-us/download/details.aspx?id=7029&quot;&gt;点此下载&lt;/a&gt;）的第 7.15.5.1 章节中有说到：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-05-09-48-13.png&quot; alt=&quot;章节&quot; /&gt;&lt;/p&gt;

&lt;p&gt;匿名函数会&lt;strong&gt;捕获当前上下文的局部变量，延长对象的生命周期；直到此委托或表达式树被回收掉。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;也就是说，只要某个方法中存在没有被回收的匿名函数/lambda 表达式/表达式树，那么当前上下文的对象直到这些匿名函数被回收之前都不会被回收，即便已经设为了 null。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/31729713/6233938&quot;&gt;c# - .NET Do lambdas prevent garbage collection of external references used in them? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.c-sharpcorner.com/ebooks/csharp-language-specification_5&quot;&gt;C# Language Specification 5.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/?wt.mc_id=MVP&quot;&gt;C# 6.0 draft Language Specification - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/anonymous-function-extends-lifetime-of-variable.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/anonymous-function-extends-lifetime-of-variable.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>实现一个 WPF 版本的 ConnectedAnimation</title>
        <description>&lt;p&gt;Windows 10 的创造者更新为开发者们带来了 Connected Animation 连接动画，这也是 Fluent Design System 的一部分。它的视觉引导性很强，用户能够在它的帮助下迅速定位操作的对象。&lt;/p&gt;

&lt;p&gt;不过，这是 UWP，而且还是 Windows 10 Creator’s Update 中才带来的特性，WPF 当然没有。于是，我自己写了一个“简易版本”。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/motion/images/connected-animations/example.gif?wt.mc_id=MVP&quot; alt=&quot;Connected Animation&quot; /&gt;&lt;br /&gt;
▲ Connected Animation 连接动画&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;模拟-uwp-中的-api&quot;&gt;模拟 UWP 中的 API&lt;/h2&gt;

&lt;p&gt;UWP 中的连接动画能跑起来的最简单代码包含下面两个部分。&lt;/p&gt;

&lt;p&gt;准备动画 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrepareToAnimate()&lt;/code&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrepareToAnimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*string */&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*UIElement */&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;开始动画 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryStart&lt;/code&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*string */&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*UIElement */&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，我们至少需要实现这些 API：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService.GetForCurrentView();&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService.PrepareToAnimate(string key, UIElement source);&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService.GetAnimation(string key);&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimation.TryStart(UIElement destination);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;实现这个-api&quot;&gt;实现这个 API&lt;/h2&gt;

&lt;p&gt;现在，我们需要写两个类才能实现上面那些方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService&lt;/code&gt; - 用来管理一个窗口内的所有连接动画&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimation&lt;/code&gt; - 用来管理和播放一个指定 Key 的连接动画&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;connectedanimationservice&quot;&gt;ConnectedAnimationService&lt;/h3&gt;

&lt;p&gt;我选用窗口作为一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService&lt;/code&gt; 的管理单元是因为我可以在一个窗口内实现这样的动画，而跨窗口的动画就非常麻烦了。所以，我试用附加属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 附加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService&lt;/code&gt; 属性，用于在任何一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 所在的地方获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationService&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;p&gt;每次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PrepareToAnimate&lt;/code&gt; 时我创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimation&lt;/code&gt; 实例来管理此次的连接动画。为了方便此后根据 Key 查找 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimation&lt;/code&gt; 的实例，我使用字典存储这些实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Annotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Media.Animation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedAnimationService&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrepareToAnimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;指定的 key 已经做好动画准备，不应该重复进行准备。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnAnimationCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAnimationCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_connectingAnimations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnimationServiceProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AnimationService&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;此 Visual 未连接到可见的视觉树中。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnimationServiceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnimationServiceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;connectedanimation&quot;&gt;ConnectedAnimation&lt;/h3&gt;

&lt;p&gt;这是连接动画的关键实现。&lt;/p&gt;

&lt;p&gt;我创建了一个内部类 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationAdorner&lt;/code&gt; 用于在 &lt;code class=&quot;highlighter-rouge&quot;&gt;AdornerLayer&lt;/code&gt; 上承载连接动画。&lt;code class=&quot;highlighter-rouge&quot;&gt;AdornerLayer&lt;/code&gt; 是 WPF 中的概念，用于在其他控件上叠加显示一些 UI，UWP 中没有这样的特性。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Adorner&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adornedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adornedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IsHitTestVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualChildrenCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetVisualChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArrangeOverride&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Arrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesiredSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdorners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;指定的 Visual 尚未连接到可见的视觉树中，找不到用于承载动画的容器。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClearFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdorners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimationAdorner&lt;/code&gt; 的作用是显示一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedVisual&lt;/code&gt;。&lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedVisual&lt;/code&gt; 包含一个源和一个目标，根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;Progress&lt;/code&gt;（进度）属性决定应该分别将源和目标显示到哪个位置，其不透明度分别是多少。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawingVisual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Progress&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnProgressChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidateProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValidateProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnProgressChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnVisualParentChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetContentBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDescendantBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetContentBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDescendantBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RenderOpen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawRectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushOpacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawRectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后，用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoubleAnimation&lt;/code&gt; 控制 &lt;code class=&quot;highlighter-rouge&quot;&gt;Progress&lt;/code&gt; 属性，来实现连接动画。&lt;/p&gt;

&lt;p&gt;完整的包含内部类的代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media.Animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Annotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Media.Animation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedAnimation&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_reportCompleted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_reportCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coordinatedElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordinatedElements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordinatedElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 正在播动画？动画播完废弃了？false&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 准备播放连接动画。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionHost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;EasingFunction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CubicEase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EasingMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EasingMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EaseInOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetTargetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Completed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;_reportCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//destination.ClearValue(UIElement.VisibilityProperty);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//destination.Visibility = Visibility.Hidden;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawingVisual&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Progress&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnProgressChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidateProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProgressProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValidateProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnProgressChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brush&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnVisualParentChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetContentBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDescendantBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetContentBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDescendantBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PointToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TopLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;PointFromScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BottomRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_sourceBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RenderOpen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawRectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_sourceBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushOpacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawRectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_destinationBrush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Adorner&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adornedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adornedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisualCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;IsHitTestVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualChildrenCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetVisualChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArrangeOverride&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Arrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DesiredSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdorners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;指定的 Visual 尚未连接到可见的视觉树中，找不到用于承载动画的容器。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClearFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdornerLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAdorners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectedAnimationAdorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adorner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;调用&quot;&gt;调用&lt;/h3&gt;

&lt;p&gt;我在一个按钮的点击事件里面尝试调用上面的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnimationButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;BeginConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectionDestination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrepareToAnimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 这里特意写在了同一个方法中，以示效果。事实上，只要是同一个窗口中的两个对象都可以实现。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 每次点击都使用不同的 Key。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-25-connected-animation-test.gif&quot; alt=&quot;连接动画试验&quot; /&gt;&lt;br /&gt;
▲ 上面的代码做的连接动画&lt;/p&gt;

&lt;h2 id=&quot;目前的局限性以及改进计划&quot;&gt;目前的局限性以及改进计划&lt;/h2&gt;

&lt;p&gt;然而稍微试试不难发现，这段代码很难将控件本身隐藏起来（设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visibility&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Collapsed&lt;/code&gt;），也就是说如果源控件和目标控件一直显示，那么动画期间就不允许隐藏（不同时显示就没有这个问题)。这样也就出不来“连接”的感觉，而是覆盖的感觉。&lt;/p&gt;

&lt;p&gt;通过修改调用方的代码，可以规避这个问题。而做法是隐藏控件本身，但对控件内部的可视元素子级进行动画。这样，动画就仅限继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Control&lt;/code&gt; 的那些元素（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;UserControl&lt;/code&gt; 了）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginConnectedAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ConnectionDestination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animatingSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animatingDestination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VisualTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectedAnimationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetForCurrentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrepareToAnimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animatingSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animatingDestination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClearValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisibilityProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ConnectionDestination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ClearValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisibilityProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-25-connected-animation-run.gif&quot; alt=&quot;连接动画试验&quot; /&gt;&lt;br /&gt;
▲ 修改后的代码做的连接动画&lt;/p&gt;

&lt;p&gt;现在，我正试图通过截图和像素着色器（Shader Effect）来实现更加通用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectedAnimation&lt;/code&gt;，正在努力编写中……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/motion/connected-animation?wt.mc_id=MVP&quot;&gt;Connected animation - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://varunshandilya.com/uwp-connected-animations-updates-with-windows-creators-release/&quot;&gt;UWP Connected Animations updates with Windows Creators release – Varun Shandilya&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lijiaxiang98.github.io/2017/09/08/%E5%AE%9E%E7%8E%B0Fluent-Design%E4%B8%AD%E7%9A%84Connected-Animation/&quot;&gt;实现 Fluent Design 中的 Connected Animation - ^ _ ^ .io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/connected-animation-of-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/connected-animation-of-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>迫不及待地体验了一把 C#8.0 中的可空引用类型（Nullable Reference）</title>
        <description>&lt;p&gt;在我之前的一篇博客 &lt;a href=&quot;/post/wipe-out-null-reference-exception&quot;&gt;NullReferenceException，就不应该存在！&lt;/a&gt; 中，我吐槽了 C# 中 null 的弊端以及避免 null 的方法；事实上这本都是现代高级语言中极力推崇的做法。Kotlin 和 Swift 自诞生之日起引用类型就不能为空，C# 背着历史的包袱直到 8.0 才开始这么做……&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装可空引用类型预览包&quot;&gt;安装可空引用类型预览包&lt;/h2&gt;

&lt;p&gt;现在 C#8.0 还没有发布，但微软已经提供了预览的扩展包，让大家体验效果并&lt;a href=&quot;https://github.com/dotnet/csharplang/wiki/Nullable-Reference-Types-Preview#feedback&quot;&gt;予以反馈&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;扩展包下载地址：&lt;a href=&quot;https://roslyninfra.blob.core.windows.net/compilers/nonnull/Roslyn_Nullable_References_Preview_11152017.zip&quot;&gt;2017年11月5日版本&lt;/a&gt; - &lt;a href=&quot;https://github.com/dotnet/csharplang/wiki/Nullable-Reference-Types-Preview#installing&quot;&gt;最新版&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下载解压后直接双击 install.bat 安装即可体验（安装前退出所有 Visual Studio）。&lt;strong&gt;这还是预览版，还有很多已知 BUG，修复后才会发布哦！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-17-42.png&quot; alt=&quot;安装&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;体验可空引用类型的作用&quot;&gt;体验可空引用类型的作用&lt;/h2&gt;

&lt;p&gt;现在，再写一个新类的时候，Visual Studio 会为我们提示非空引用类型未初始化，并给出建议。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-22-30.png&quot; alt=&quot;建议&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-24-34.png&quot; alt=&quot;修改&quot; /&gt;&lt;/p&gt;

&lt;p&gt;采纳它的建议，生成构造函数：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-25-23.png&quot; alt=&quot;生成构造函数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-25-55.png&quot; alt=&quot;生成的构造函数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果我们认为这个属性可以为 null，那么就可以添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; 使此属性的类型变为可空引用类型。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-27-44.png&quot; alt=&quot;可空引用类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，如果在非 null 的地方使用此属性，则会要求判空。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-29-35.png&quot; alt=&quot;可空引用类型使用前需要判空&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;丢不掉的兼容性包袱&quot;&gt;丢不掉的兼容性包袱&lt;/h2&gt;

&lt;p&gt;由于有兼容性的包袱&lt;em&gt;（至少得让你写了数月几年的项目编译通过吧）&lt;/em&gt;，所以 C#8.0 的可空引用类型仅仅是“&lt;strong&gt;契约&lt;/strong&gt;”的作用，并不能在编译级别阻止对非空引用类型的 null 赋值。而且目前为止也没有提供编译级别报错的选项。&lt;/p&gt;

&lt;p&gt;已有的程序集没有标记那些非空哪些可空，那么 C#8.0 又怎么看呢&lt;em&gt;（其实应该问 Roslyn 怎么看）&lt;/em&gt;？它只能默认所有的类型都是非空的，于是会给你警告，就像这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-18-21-39-41.png&quot; alt=&quot;已有程序集的警告&quot; /&gt;&lt;/p&gt;

&lt;p&gt;很明显，&lt;code class=&quot;highlighter-rouge&quot;&gt;string.IsNullOrEmpty&lt;/code&gt; 是接受 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 值的，然而改不了现有程序集，于是这样的标记也没有用。&lt;/p&gt;

&lt;p&gt;从现在看来，我们只能把它当作 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts?wt.mc_id=MVP&quot;&gt;Code Contracts&lt;/a&gt; 的语法版本。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Nullable Reference
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/wiki/Nullable-Reference-Types-Preview&quot;&gt;Nullable Reference Types Preview · dotnet/csharplang Wiki&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.c-sharpcorner.com/article/whats-new-in-c-sharp-7-1-and-7-2/&quot;&gt;What’s New In C# 7.1 And 7.2&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/nullable-reference-in-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/nullable-reference-in-csharp.html</guid>
        
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 自定义键盘焦点样式（FocusVisualStyle）</title>
        <description>&lt;p&gt;WPF 自带的键盘焦点样式是与传统控件样式搭配的，但 WPF 凭着其强大的自定义样式的能力，做出与传统控件样式完全不同风格的 UI 简直易如反掌。这时，其自带的键盘焦点样式（&lt;code class=&quot;highlighter-rouge&quot;&gt;FocusVisualStyle&lt;/code&gt;）就非常不搭了，改改会舒服得多。比如，改成 UWP 的样式。&lt;/p&gt;

&lt;p&gt;本文将展示 WPF 自定义键盘焦点样式自定义的&lt;strong&gt;坑&lt;/strong&gt;！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-17-wpf-default-focus-visual-style.gif&quot; alt=&quot;WPF 自带的键盘焦点样式&quot; /&gt;&lt;br /&gt;
▲ WPF 自带的键盘焦点样式&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-17-uwp-dark-focus-visual-style.gif&quot; alt=&quot;UWP 暗主题键盘焦点样式&quot; /&gt;&lt;br /&gt;
▲ UWP 暗主题键盘焦点样式&lt;/p&gt;

&lt;p&gt;其实微软官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/styling-for-focus-in-controls-and-focusvisualstyle?wt.mc_id=MVP&quot;&gt;Styling for Focus in Controls, and FocusVisualStyle - Microsoft Docs&lt;/a&gt; 有说明 &lt;code class=&quot;highlighter-rouge&quot;&gt;FocusVisualStyle&lt;/code&gt;，但是——&lt;strong&gt;完全没有讲自定义&lt;/strong&gt;好吗！&lt;/p&gt;

&lt;p&gt;所以，我试着写一个样式以覆盖默认的样式：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Static SystemParameters.FocusVisualStyleKey}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Control.Template&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ControlTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-3&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrokeThickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stroke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gray&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SnapsToDevicePixels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ControlTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行一看，结果完全没有效果……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-17-wpf-default-focus-visual-style.gif&quot; alt=&quot;完全没有效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;StackOverflow 上也有人说了这件事：&lt;a href=&quot;https://stackoverflow.com/questions/29101942/how-to-redefine-focusvisualstyle-for-a-wpf-user-control&quot;&gt;xaml - How to redefine FocusVisualStyle for a WPF user control - Stack Overflow&lt;/a&gt;。&lt;a href=&quot;https://stackoverflow.com/users/632337/rohit-vats&quot;&gt;Rohit Vats&lt;/a&gt; 说需要通过单独为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt; 设置才能生效并在回答中贴出了代码。&lt;/p&gt;

&lt;p&gt;然而同样的代码应用到项目中，我们会发现，我们此前定义的无 Key 样式也失效了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-17-15-27-27.png&quot; alt=&quot;样式失效&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我的代码是这样的，试图用上此前定义的无 Key 样式，只是无效。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Static SystemParameters.FocusVisualStyleKey}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Control.Template&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ControlTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-3&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrokeThickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Stroke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Gray&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SnapsToDevicePixels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ControlTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasedOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource {x:Type Button}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FocusVisualStyle&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，有没有办法能够一次定义整个应用程序生效呢？&lt;/p&gt;

&lt;p&gt;答案是——&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-17-15-32-09.png&quot; alt=&quot;没有&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1879526/change-the-focusvisualstyle-in-the-entire-application&quot;&gt;wpf - Change the FocusVisualStyle in the entire application - Stack Overflow&lt;/a&gt; 也承认了这一点。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;所以，当希望为 WPF 程序自定义 &lt;code class=&quot;highlighter-rouge&quot;&gt;FocusVisualStyle&lt;/code&gt; 样式的话，建议从零开始，定义每一个最底层样式的时候设置好 &lt;code class=&quot;highlighter-rouge&quot;&gt;FocusVisualStyle&lt;/code&gt;，其他样式定义的时候继承自最底层样式。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/styling-for-focus-in-controls-and-focusvisualstyle?wt.mc_id=MVP&quot;&gt;Styling for Focus in Controls, and FocusVisualStyle - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/29101942/how-to-redefine-focusvisualstyle-for-a-wpf-user-control&quot;&gt;xaml - How to redefine FocusVisualStyle for a WPF user control - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1879526/change-the-focusvisualstyle-in-the-entire-application&quot;&gt;wpf - Change the FocusVisualStyle in the entire application - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/customize-focus-visual-style.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/customize-focus-visual-style.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>异步任务中的重新进入（Reentrancy）</title>
        <description>&lt;p&gt;一个按钮，点击执行一个任务。我们可能直接在它的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Click&lt;/code&gt; 事件中写下了执行任务的代码。&lt;/p&gt;

&lt;p&gt;一般我们无需担心这样的代码会出现什么问题——&lt;strong&gt;但是，这样的好事情只对同步任务有效；一旦进入了异步世界，这便是无尽的 BUG！&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;重新进入reentrancy&quot;&gt;重新进入（Reentrancy）&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Button_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 同步任务。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 以上，在按钮点击事件中执行同步任务&lt;/p&gt;

&lt;p&gt;上面的代码，无论我们在界面上多么疯狂地点击按钮，因为 UI 会在任务执行的过程中停止响应，所以 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomething&lt;/code&gt; 只会依次执行（还会偶尔忽略一些）。这通常不会造成什么问题，但如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomething&lt;/code&gt; 变成异步的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomethingAsync&lt;/code&gt;（就像下面那样），那么情况就变得不同了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Button_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 异步任务。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 以上，在按钮点击事件中执行异步任务&lt;/p&gt;

&lt;p&gt;由于任务执行的过程中 UI 依然是响应的，&lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomethingAsync&lt;/code&gt; 会因此在每一次点击的时候都进入。&lt;strong&gt;在异步任务结束之前重新进入此异步任务的过程，叫做重新进入（Reentrancy）。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;重新进入的五种方式&quot;&gt;重新进入的五种方式&lt;/h2&gt;

&lt;p&gt;微软在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/handling-reentrancy-in-async-apps?wt.mc_id=MVP&quot;&gt;Handling Reentrancy in Async Apps (C#)&lt;/a&gt; 一文中给出了重新进入的三种方式：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;禁用“开始”按钮&lt;/li&gt;
    &lt;li&gt;取消和重启操作&lt;/li&gt;
    &lt;li&gt;运行多个操作并将输出排入队列&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;从语言描述中就能知道除了第 2 点看起来具有通用性外，其他两点只为了解决文章中面临的“输出网页列表”问题。第 1 点其思想可以重用，但第 3 点就很难抽取公共的重新进入思想。于是，我总结其前两点，再额外补充两种重新进入的方式，和不处理一起作为五种不同的处理方法。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;禁用重新进入&lt;/li&gt;
  &lt;li&gt;并发&lt;/li&gt;
  &lt;li&gt;取消然后重启操作&lt;/li&gt;
  &lt;li&gt;将异步任务放入队列中依次执行&lt;/li&gt;
  &lt;li&gt;仅执行第一次和最后一次&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;禁用重新进入&quot;&gt;禁用重新进入&lt;/h3&gt;

&lt;p&gt;禁用是最直接最简单也最彻底的重新进入问题解决办法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;既然重新进入可能出问题，那我们就禁止重新进入好了……&lt;/p&gt;

&lt;h3 id=&quot;并发&quot;&gt;并发&lt;/h3&gt;

&lt;p&gt;当然，不处理也是一种方法。这意味着我们需要真的考虑 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoSomethingAsync&lt;/code&gt; 并发造成的影响。&lt;/p&gt;

&lt;h3 id=&quot;取消然后重启操作&quot;&gt;取消然后重启操作&lt;/h3&gt;

&lt;p&gt;取消，然后重新执行一次，这也是常见的重新进入类型。浏览器或者资讯类 APP 中的刷新功能就是这种重新进入方式最常见的应用场景，用户重新执行一次刷新，可能因为前面那一次（因为网络问题或其他原因）太慢，所以重新开始。&lt;/p&gt;

&lt;h3 id=&quot;将异步任务放入队列中依次执行&quot;&gt;将异步任务放入队列中依次执行&lt;/h3&gt;

&lt;p&gt;放入队列中是因为此异步任务的顺序是很重要的，要求每一次执行且保持顺序一致。典型的应用场景是每一次执行都需要获取或生成一组数据输出（到屏幕、文件或者其他地方）。&lt;/p&gt;

&lt;h3 id=&quot;仅执行第一次和最后一次&quot;&gt;仅执行第一次和最后一次&lt;/h3&gt;

&lt;p&gt;如果用户每一次执行此异步任务都会获取当前应用程序的最新状态，然后根据最新状态执行；那么如果状态更新了，对旧状态执行多少次都是浪费的。&lt;/p&gt;

&lt;p&gt;比如保存文件的操作。第一次进入异步任务的时候会进行保存，如果保存过程没有结束又触发新的保存，则等上一次保存结束之后再执行保存操作即可。而如果第一次保存没有结束的时候又触发非常多次的保存，也只需要在第一次结束之后再保存一次即可，毕竟既然最后一次保存时的状态已经是最新状态，不需要再把之前旧的状态保存一次。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/handling-reentrancy-in-async-apps?wt.mc_id=MVP&quot;&gt;Handling Reentrancy in Async Apps (C#) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/handling-reentrancy-in-async-apps?wt.mc_id=MVP&quot;&gt;处理异步应用中的重新进入 (C#) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/reentrancy-in-async-method.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/reentrancy-in-async-method.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>将 UWP 的有效像素（Effective Pixels）引入 WPF</title>
        <description>&lt;p&gt;在很久很久以前，WPF 诞生之初，有一个神奇的单位，它的名字叫做——设备无关单位（DIP，Device Independent Unit）。微软给它描绘了一片美好的愿景——在任何显示器上显示的尺寸是相同的。&lt;/p&gt;

&lt;p&gt;What the ** is this unit!!! 神 TM 相同！！！&lt;/p&gt;

&lt;p&gt;UWP 采用有效像素（Effective Pixels）来描述尺寸，这是才是能够自圆其说的一套尺寸描述；WPF 的尺寸机制与 UWP 完全就是同一套，使用有效像素才能解释 WPF 尺寸变化上的各种特性！&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;统一概念&quot;&gt;统一概念&lt;/h2&gt;

&lt;p&gt;在继续讨论之前，我们必须统一几个概念。不能说那些意义不明确的词，尤其是“宽高”“大小”“尺寸”“更大”。试想你说一个按钮的宽高是 200，那么它的宽高到底是多少呢？一个屏幕上的按钮和另一个屏幕上的按钮哪个更大呢？&lt;/p&gt;

&lt;p&gt;在本文中，对于尺寸，我们只说三个概念：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;物理尺寸（单位：厘米）&lt;/li&gt;
  &lt;li&gt;显示器像素个数（单位：个）&lt;/li&gt;
  &lt;li&gt;有效像素（即 WPF 中最常用的那个单位；在本文结束之前，这应该是一个未定义的概念）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果我们说 A 按钮比 B 按钮的物理宽度更大，那么无论它们显示在哪个显示器上，都具备相同的关系——因为我们可以拿尺子来量。&lt;/p&gt;

&lt;p&gt;如果我们说 A 按钮比 B 按钮在宽度上占用的显示器像素个数更多，我们也可以拿放大镜去屏幕上一个点一个点地数——当然，各种截图工具已经在&lt;strong&gt;最佳分辨率&lt;/strong&gt;下具备数像素个数的功能了（这里一定要突出最佳分辨率）。&lt;/p&gt;

&lt;p&gt;而有效像素（Effective Pixels，本文记其为 epx）就是本文从 UWP 中引入的尺寸概念。当我们说按钮的有效像素宽度为 200 时，指的是你在 WPF 的 XAML 或 C# 代码中写下了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width=&quot;200&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;接下来，当我们谈论尺寸时，只会用以上三个概念进行比较，而不会再用模糊不清的尺寸名词。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;从愿景看有效像素的意义&quot;&gt;从愿景看有效像素的意义&lt;/h2&gt;

&lt;p&gt;有效像素单位的诞生一定是为了解决某种尺寸问题，而且是因为现有的尺寸单位无法简单地描述这一问题。而我们就要准确描述这一问题，并将得到的单位定义成“有效像素”。&lt;/p&gt;

&lt;h3 id=&quot;吐槽-dip&quot;&gt;吐槽 DIP&lt;/h3&gt;

&lt;p&gt;WPF 曾经说自己用的是“设备无关单位”（DIP），愿景是在所有显示器上显示的物理尺寸相同。比如你在代码中写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width=&quot;96&quot;&lt;/code&gt; 的按钮，那么在所有显示器上其尺寸为 1 英寸。&lt;/p&gt;

&lt;p&gt;其实简单测试就不难发现这是一个根本无法自圆其说的愿景，具体无法自圆其说的点有两个。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;WPF 说自己的开发无需考虑 DPI 缩放，因为它会自己做缩放。那么当你写下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width=&quot;96&quot;&lt;/code&gt; 时，到底缩放还是不缩放呢？缩放就迁就了 DPI 缩放的特性，违背了物理尺寸相同的特性；不缩放就迁就了物理尺寸相同的特性，丢失了 DPI 缩放的特性。&lt;/li&gt;
  &lt;li&gt;在非 PC 设备（手机、平板电脑、大屏幕电视）上，如果依然保持物理尺寸相同，那么 PC 上显示合适的 3cm 的按钮在手机上将占据大半个屏幕，在电视上将小得几乎看不见。怎么能让一个 UI 框架做出这么脑残的设计呢？&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;有效像素epx的愿景&quot;&gt;有效像素（epx）的愿景&lt;/h3&gt;

&lt;p&gt;有效像素概念的出现，就摒除了 WPF 物理尺寸相同这样荒谬而无法自圆其说的设定。但为了给有效像素设下定义，我们来看看微软到底期望这样的尺寸单位带来哪些方便吧：&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe class=&quot;video&quot; src=&quot;https://www.youtube.com/embed/X_03JKvnIls&quot; frameborder=&quot;0&quot; gesture=&quot;media&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;▲ 如果此处看不到视频，请前往 Channel 9 观看：&lt;a href=&quot;https://channel9.msdn.com/Blogs/One-Dev-Minute/Designing-Universal-Windows-Platform-apps&quot;&gt;Designing Universal Windows Platform apps&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;具体说来，对于手机和平板电脑（笔记本、Surface）这些近距离观看的设备，其物理尺寸可以更小；对于客厅摆放的大屏幕电视，观看距离较远，物理尺寸应该更大。相同的界面元素在不同设备上显示时，呈现出来的效果在视野角度上是相近的，这才是人眼观看比较舒适的尺寸概念的设计。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-14-09-48-42.png&quot; alt=&quot;视野角度相近&quot; /&gt;&lt;br /&gt;
▲ 图片来自于微软 UWP 设计指导文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/design/basics/design-and-ui-intro?wt.mc_id=MVP&quot;&gt;Introduction to Universal Windows Platform (UWP) app design (Windows apps)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可以看出，有效像素的出现解决了我在 &lt;a href=&quot;#%E5%90%90%E6%A7%BD-dip&quot;&gt;以上吐槽&lt;/a&gt; 中列举出无法自圆其说的第 2 点。认识到一个优秀的屏幕显示单位并不是按物理尺寸定义，而是根据不同的使用场景有所不同。第 1 点也部分得到了缓解——接受 DPI 缩放的特性，放弃承认物理尺寸相同的设定。&lt;/p&gt;

&lt;h2 id=&quot;有效像素epx的局限性&quot;&gt;有效像素（epx）的局限性&lt;/h2&gt;

&lt;p&gt;一个好的概念除了要充分展示自己的愿景，也要看清自己的局限性。&lt;/p&gt;

&lt;p&gt;而有效像素的局限性就在于——它的愿景只是&lt;strong&gt;理想状态&lt;/strong&gt;下才能有的效果，而微软本身允许硬件厂商和用户进行设置以偏离理想状态。&lt;/p&gt;

&lt;p&gt;具体是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;显示器厂商为了自己的销售目标，会推出各种各样的型号——有高端的，有低端的，有主打性价比的。而性价比的主要来源就是——“噱头”——在部分参数上非常漂亮，部分参数却缩水严重。比如同样是 34 寸的 21:9 超宽屏显示器，5999 元的可以是 3440×1440 像素个数，而 3999 元的就只能是 2560×1080 像素个数。前者每英寸像素点数接近 96，而后者则低多了。Windows 操作系统上支持的最低 DPI 设置只能是 96 了，不能再低；以至于后者实际上在相同观看距离上比前者显示的界面元素的物理尺寸会大很多。这是硬件厂商的销售策略，你一个虚拟的单位还能拿它怎么样！&lt;br /&gt;
这其实是此愿景不能实现的最主要原因了——各大显示器厂商都存在不按照最佳观看效果设置显示器参数的问题。这也是为什么我们经常能发现有些笔记本上的图标和字体大小被默认设置得小得可怜，或者超大屏幕设备上文字小得远处看不清的原因。（这里我们只谈显示器厂商在 EDID 信息中设置的最佳时序，这个时序欺骗了操作系统使之给出了不合适的显示效果；不要说用户可以改的问题，毕竟让用户改已经提高了门槛了。）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;用户可以随时修改屏幕显示分辨率，修改系统或屏幕的 DPI 值。在显示分辨率与显示器实际物理分辨率不一致的情况下，用户还能设置画面的填充方式（居中或是拉伸）。&lt;br /&gt;
我们认为，用户主动设置 DPI 是出于自己的一些目的——希望放大或缩小界面元素，而这有可能是因为原有大小对用户自己来说看起来不够舒适。（这里不想吐槽设置分辨率还不设置为居中显示的用户，那种画面模糊的感觉，怎么能承受！）&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;事实上，目前为止，只有一款设备真正达到了微软期望中的理想状态，那就是——Surface Studio！其它都只能算作接近，或者连接近都算不上。&lt;/p&gt;

&lt;h2 id=&quot;给有效像素下个定义&quot;&gt;给有效像素下个定义&lt;/h2&gt;

&lt;p&gt;结合微软对有效像素的愿景，结合实际情况，我认为“有效像素”的定义应该是这样的：&lt;/p&gt;

&lt;p&gt;在理想状态下，&lt;strong&gt;1 有效像素等于用户观看距离 50cm 时，观看屏幕上 1/96 英寸的物理距离所对应的视角大小&lt;/strong&gt;。&lt;br /&gt;
非理想状态下，&lt;strong&gt;1 有效像素等于显示器点对点显示像素时，1 屏幕像素乘以系统 DPI 值除以 96&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;WPF 和 UWP 的尺寸单位都可以用有效像素来理解，而这本身就是它们两个框架内建的单位系统。（彻底抛弃那个不能自圆其说的 DIP 吧！）&lt;/p&gt;

&lt;h2 id=&quot;有效像素的特性&quot;&gt;有效像素的特性&lt;/h2&gt;

&lt;p&gt;在以上定义之下，再研究有效像素的特性时，我们便能接受那些非理想状态下的不同行为，不再像 WPF 的 DIP 那样绝对而富有争议。&lt;/p&gt;

&lt;h3 id=&quot;按钮的大小之争&quot;&gt;按钮的大小之争&lt;/h3&gt;

&lt;p&gt;当我们在代码中写下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width=&quot;96&quot;&lt;/code&gt; 时，这个按钮到底多大？&lt;/p&gt;

&lt;p&gt;谈物理尺寸：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在 Surface Studio 这样的理想设备上，如果用户没有胡乱设置，它的物理宽度是 1 英寸；&lt;/li&gt;
  &lt;li&gt;在同一个显示器设备上，如果显示器的 PPI 是 96 pixels/inch，且用户使用最佳分辨率
    &lt;ul&gt;
      &lt;li&gt;DPI 值设置为 96，则它的物理宽度是 1 英寸&lt;/li&gt;
      &lt;li&gt;DPI 值设置为 144，则它的物理宽度是 1.5 英寸&lt;/li&gt;
      &lt;li&gt;DPI 值设置为 192，则它的物理宽度是 2 英寸&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;在以上情况下，如果 DPI 值固定为 96，但用户降低了分辨率
    &lt;ul&gt;
      &lt;li&gt;居中点对点显示，则它的物理宽度是 1 英寸&lt;/li&gt;
      &lt;li&gt;拉伸显示，则它的物理宽度大于 1 英寸&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;换一台显示器，PPI 值更大，则相同情况下的每一种情况都比以上物理宽度更小&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;谈显示器像素个数：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;用户使用了最佳分辨率
    &lt;ul&gt;
      &lt;li&gt;在 DPI 值为 96 时，显示完按钮宽度所用的屏幕像素个数为 96&lt;/li&gt;
      &lt;li&gt;DPI 值设置为 192 时，则显示完按钮宽度所用的屏幕像素个数是 192&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;在以上情况下，如果用户降低了分辨率
    &lt;ul&gt;
      &lt;li&gt;居中点对点显示，显示完按钮宽度所用的屏幕像素个数为 96&lt;/li&gt;
      &lt;li&gt;拉伸显示，显示完按钮宽度所用的屏幕像素个数大于为 96，虚拟的系统像素个数依然等于 96&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;接受现实&quot;&gt;接受现实&lt;/h3&gt;

&lt;p&gt;看看按钮实际的大小，你会发现，影响因素真的太多太多了！当我们不再沉浸在 DIP 的理想中，不再纠结有效像素的愿景的时候，便能觉得有效像素其实为我们考虑 DPI 缩放问题做了不少努力，确实能减轻我们 UI 的开发工作量。&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/introduce-uwp-effective-pixels-into-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/introduce-uwp-effective-pixels-into-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>WPF 跨应用程序域的 UI（Cross AppDomain UI）</title>
        <description>&lt;p&gt;为自己写的程序添加插件真的是一个相当常见的功能，然而如果只是简单加载程序集然后去执行程序集中的代码，会让宿主应用程序暴露在非常危险的境地！因为只要插件能够运行任何一行代码，就能将宿主应用程序修改得天翻地覆哭爹喊娘；而根本原因，就在于暴露了整个托管堆和整个 UI 树。&lt;/p&gt;

&lt;p&gt;如果将宿主和插件放到不同的应用程序域中，则可以解决此问题。本文将介绍跨应用程序域承载 UI 的方法，其中也包含跨域（Cross-Domain）调用方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;来自于托管插件框架的辅助类&quot;&gt;来自于托管插件框架的辅助类&lt;/h2&gt;

&lt;p&gt;.NET Framework 自 3.5 以来推出了托管插件框架（MAF，Managed AddIn Framework），位于 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.AddIn&lt;/code&gt; 命名空间。其特性在于，将宿主和插件隔离在不同的应用程序域中，避免插件对宿主造成不良影响。&lt;/p&gt;

&lt;p&gt;此命名空间中存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElementAdapters&lt;/code&gt; 类型，在 System.Windows.Presentation 程序集中，详见 &lt;a href=&quot;http://referencesource.microsoft.com/#System.Windows.Presentation/System/AddIn/Pipeline/FrameworkElementAdapters.cs&quot;&gt;FrameworkElementAdapters.cs&lt;/a&gt;。虽说主要用于 MAF 插件框架，但其实只需要此类型便可以实现跨应用程序域的 UI。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElementAdapters&lt;/code&gt; 只有两个方法，&lt;code class=&quot;highlighter-rouge&quot;&gt;ViewToContractAdapter&lt;/code&gt; 将 UI 转换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;INativeHandleContract&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContractToViewAdapter&lt;/code&gt; 将 &lt;code class=&quot;highlighter-rouge&quot;&gt;INativeHandleContract&lt;/code&gt; 用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 进行承载。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FrameworkElementAdapters&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContractToViewAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INativeHandleContract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nativeHandleContract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INativeHandleContract&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ViewToContractAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个极简的跨域-ui-demo&quot;&gt;一个极简的跨域 UI Demo&lt;/h2&gt;

&lt;p&gt;首先，我们需要有一个支持跨域调用的类型，并有任意的可以用来返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;INativeHandleContract&lt;/code&gt; 的方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DomainX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MarshalByRefObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INativeHandleContract&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElementAdapters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ViewToContractAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Fill&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForestGreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们需要跨域创建这个 UI 控件并得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;INativeHandleContract&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DomainX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstanceAndUnwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DomainX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DomainX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contract&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，在需要承载这个跨域 UI 的地方取得这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;INativeHandleContract&lt;/code&gt; 的实例 &lt;code class=&quot;highlighter-rouge&quot;&gt;contract&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElementAsyncAdapters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContractToViewAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// this 在这里是 MainWindow 或者 MainPage，或者其它任何能够承载 FrameworkElement 的对象。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的这两端代码都可以写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Loaded&lt;/code&gt; 事件中。&lt;/p&gt;

&lt;h2 id=&quot;对-maf-吐一下槽&quot;&gt;对 MAF 吐一下槽&lt;/h2&gt;

&lt;p&gt;MAF 框架对插件和宿主程序集所在的文件夹结构有要求。这可是非常讨厌的一项特性！因为当我们希望采用 MAF 框架的时候，我们的应用程序可能已经有自己独特的一套目录了。就算我们从零开始写应用，采用 MAF 约定的方式组织 dll 也是很丑的方式（带有很重的 MAF 的影子）。&lt;/p&gt;

&lt;p&gt;它没有提供任何的配置，而且如果不按照约定放置文件夹，还会发生如下错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-13-13-23-53.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ENikS/System.AddIn&quot;&gt;ENikS/System.AddIn: Projects related to Microsoft System.AddIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/clraddins/2008/02/22/add-in-performance-what-can-you-expect-as-you-cross-an-isolation-boundary-and-how-to-make-it-better-jesse-kaplan/&quot;&gt;Add-In Performance: What can you expect as you cross an isolation boundary and how to make it better [Jesse Kaplan] – CLR Add-In Team Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/app-development/wpf-add-ins-overview?wt.mc_id=MVP&quot;&gt;WPF Add-Ins Overview - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/add-ins/walkthrough-create-extensible-app?wt.mc_id=MVP&quot;&gt;Walkthrough: Creating an Extensible Application - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/add-ins/?wt.mc_id=MVP&quot;&gt;Add-ins and Extensibility - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-cross-domain-ui.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-cross-domain-ui.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Visual-&gt;UIElement-&gt;FrameworkElement，带来更多功能的同时也带来了更多的限制</title>
        <description>&lt;p&gt;在 WPF 或 UWP 中，我们平时开发所遇到的那些 UI 控件或组件，都直接或间接继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Framework&lt;/code&gt;。例如：&lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;StackPanel&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Canvas&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Image&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Slider&lt;/code&gt;。我们总会自然而然地认为这些控件都是有大小的，它们会在合适的位置显示自己，通常不会超出去。但是，&lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 甚至是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Control&lt;/code&gt; 用得久了，都开始忘记 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 带给我们的那些自由。&lt;/p&gt;

&lt;p&gt;阅读本文将了解我们熟知的那些功能以及限制的由来，让我们站在限制之外再来审视 WPF 的可视化树，再来看清 WPF 各种控件属性的本质。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;宽度和高度&quot;&gt;宽度和高度&lt;/h2&gt;

&lt;p&gt;如果问 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt; 属性来自谁，只要在 WPF 和 UWP 里混了一点儿时间都会知道——&lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt;。随着 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 的宽高属性一起带来的还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;ActualWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ActualHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MinWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MinHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxHeight&lt;/code&gt;。正是这些属性的存在，让我们可以直观地给元素指定尺寸——想设置多少就设置多少。&lt;/p&gt;

&lt;p&gt;然而……当你把宽或高设置得比父容器允许的最大宽高还要大的时候呢？我们会发现，控件被“切掉”了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-13-23-13-39.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 被切掉的椭圆&lt;/p&gt;

&lt;p&gt;然而，&lt;strong&gt;因布局被“切掉”这一特性也是来自于 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt;&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 布局时即便空间不够也不会故意去将超出边界的部分切掉，这一点从其源码就能得到证明：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// This method supplies an additional (to the &amp;lt;see cref=&quot;Clip&quot;/&amp;gt; property) clip geometry&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// that is used to intersect Clip in case if &amp;lt;see cref=&quot;ClipToBounds&quot;/&amp;gt; property is set to &quot;true&quot;.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Typically, this is a size of layout space given to the UIElement.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;Geometry to use as additional clip if ClipToBounds=true&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Geometry&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetLayoutClip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layoutSlotSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClipToBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RectangleGeometry&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RectangleGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Freeze&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClipToBounds&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 的时候进行矩形切割。&lt;/p&gt;

&lt;p&gt;然而 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 的切掉逻辑就复杂多了，鉴于有上百行，就只贴出链接 &lt;a href=&quot;http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/FrameworkElement.cs,4400104dde3195fa&quot;&gt;FrameworkElement.GetLayoutClip&lt;/a&gt;。其处理了各种布局、变换过程中的情况。&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 的出现是为了让我们编程中像对待一个有固定尺寸的物体一样，所以也在切除上模拟了这样的空间有限的效果。&lt;/p&gt;

&lt;p&gt;如果希望不被切掉，有两种方法修正：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;确保布局的时候所需尺寸不大于可用尺寸（一点也不能大于，就算是 &lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt; 精度问题导致的细微偏大都不行）
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureOverride&lt;/code&gt; 返回的尺寸不大于参数传入的尺寸&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeOverride&lt;/code&gt; 返回的尺寸不大于参数传入的尺寸&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetLayoutClip&lt;/code&gt; 方法，并返回 null（或者写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 那样）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;布局系统&quot;&gt;布局系统&lt;/h2&gt;

&lt;p&gt;提及 &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureOverride&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeOverride&lt;/code&gt;，大家都会认为这是 WPF 布局系统给我们提供的两个可供重写的方法。然而，这两个方法其实也是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 才提供的。&lt;/p&gt;

&lt;p&gt;真正布局的方法是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Measure&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Arrange&lt;/code&gt;，而可供重写的方法是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureCore&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeCore&lt;/code&gt;。这两组方法均来自于 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt;，而布局系统其实是 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 引入的。&lt;/p&gt;

&lt;p&gt;那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 做了什么呢？它密封了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureCore&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeCore&lt;/code&gt; 这两个布局的重写方法，以便能够处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MinWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MinHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Margin&lt;/code&gt; 这些属性对布局的影响。&lt;/p&gt;

&lt;p&gt;你觉得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt; 属性是元素的最终宽高吗？我们在 &lt;a href=&quot;#%E5%AE%BD%E5%BA%A6%E5%92%8C%E9%AB%98%E5%BA%A6&quot;&gt;宽度和高度&lt;/a&gt; 一节中已经说了不是，前面一段也说了不是——&lt;strong&gt;它们真的只是布局属性&lt;/strong&gt;！然而，这真的很容易形成误解！&lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt; 属性其实和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MinWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MinHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxHeight&lt;/code&gt; 是完全一样的用途，只是在布局过程中为计算最终尺寸提供的布局限制而已。只不过 &lt;code class=&quot;highlighter-rouge&quot;&gt;MinWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MinHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxHeight&lt;/code&gt; 用大于和小于进行尺寸的限制，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt; 用等于进行尺寸的限制。最终的尺寸依然是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ActualWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ActualHeight&lt;/code&gt;，而这个值跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderSize&lt;/code&gt; 其实是一个意思，因为内部获取的就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderSize&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;值得注意的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;ActualWidth&lt;/code&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ActualHeight&lt;/code&gt; 与 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderSize&lt;/code&gt; 一样，是布局结束后才会更新的，开发中需要如果修改了属性立即获取这些值其实必然是旧的，拿这些值进行计算会造成错误的尺寸数据。&lt;/p&gt;

&lt;p&gt;顺便吐槽一下：&lt;em&gt;其实微软是喜欢用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Core&lt;/code&gt; 来作为子类重写方法的后缀的，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;EasingFunction&lt;/code&gt; 都是用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Core&lt;/code&gt; 后缀来处理重写。&lt;code class=&quot;highlighter-rouge&quot;&gt;Override&lt;/code&gt; 后缀纯属是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 把这个名字用了而已。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;屏幕交互&quot;&gt;屏幕交互&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 中存在着布局计算，&lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 中存在着带限制的布局计算，这很容易让人以为屏幕相关的坐标计算会存在于 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;然而其实 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 只涉及到控件之间的坐标计算（&lt;code class=&quot;highlighter-rouge&quot;&gt;TranslatePoint&lt;/code&gt;），真正涉及到屏幕坐标的转换是位于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 中的，典型的是这几个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TransformToAncestor&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TransformToDescendant&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TransformToVisual&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PointFromScreen&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PointToScreen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以其实如果希望做出非常轻量级的高性能 UI，继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 也是一个大胆的选择。&lt;em&gt;当然，真正遇到瓶颈的时候，继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 也解决不了多少问题。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;样式和模板&quot;&gt;样式和模板&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 开始有了样式（&lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt;），&lt;code class=&quot;highlighter-rouge&quot;&gt;Control&lt;/code&gt; 开始有了模板（&lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt;）。而模板极大地方便了样式定制的同时，也造成了强大的性能开销，因为本来的一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 瞬间变成了几个、几十个。一般情况下这根本不会是性能瓶颈，然而当这种控件会一次性产生几十个甚至数百个（例如表格）的时候，这种瓶颈就会非常明显。&lt;/p&gt;

&lt;h2 id=&quot;总结容易出现理解偏差的几个点&quot;&gt;总结容易出现理解偏差的几个点&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Width&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Height&lt;/code&gt; 属性其实只是为布局过程中的计算进行限制而已，跟 &lt;code class=&quot;highlighter-rouge&quot;&gt;MinWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MinHeight&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxWidth&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;MaxHeight&lt;/code&gt; 没有区别，并不直接决定实际尺寸。&lt;/li&gt;
  &lt;li&gt;如果发现元素布局中被切掉了，这并不是不可避免的问题；因为切掉是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 为我们引入的特性，不喜欢可以随时关掉。&lt;/li&gt;
  &lt;li&gt;微软对于子类重写核心逻辑的方法喜欢使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Core&lt;/code&gt; 后缀，布局中用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Override&lt;/code&gt; 只是因为名字被占用了。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 就可以计算与屏幕坐标之间的转换。&lt;/li&gt;
  &lt;li&gt;模板（&lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt;）会额外产生很多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;，有可能会成为性能瓶颈。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/wpf-architecture?wt.mc_id=MVP&quot;&gt;WPF Architecture - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/features-and-limits-on-visual-uielement-frameworkelement.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/features-and-limits-on-visual-uielement-frameworkelement.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>DependencyProperty.UnsetValue 的正确打开方式</title>
        <description>&lt;p&gt;无论是 WPF，还是 UWP，只要你用了绑定或者标记扩展，一定会碰到一个神奇的值——&lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;。&lt;code class=&quot;highlighter-rouge&quot;&gt;UnsetValue&lt;/code&gt; 是什么意思？为什么会出现这个值呢？如果要让 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsetValue&lt;/code&gt; 为我们所用，正确的用法又是什么呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dependencypropertyunsetvalue-是什么&quot;&gt;DependencyProperty.UnsetValue 是什么？&lt;/h2&gt;

&lt;p&gt;要知道这是什么，一定要看源码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt; Standard unset value &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UnsetValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NamedObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DependencyProperty.UnsetValue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;NamedObject&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;NamedObject&lt;/code&gt; 又是什么呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NamedObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NamedObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'{'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{{{0}}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;好吧，其实这个类根本就没有什么用途，微软只是随便找了一个类，以便你在 Visual Studio 调试器或者你自己用代码输出值的时候能够显示一个预设好的字符串。真的只是起调试作用的啊！&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 的定义中，只是为了让大家调试的时候显示 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 而已。值本身不代表任何意义，只是为了说明遇到了一个“未设置”的值。&lt;/p&gt;

&lt;p&gt;但是有人会问：&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 在调试的时候也会显示 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 啊，为啥不用 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，要特别准备一个值呢？&lt;/p&gt;

&lt;p&gt;这是因为在绑定中，&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 可能是一个合理的值，可能会被故意用在绑定中来达到某种目的。于是微软必须用一个大家平常开发中一定不会用到的值来表示“不合理”，于是祭出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;什么情况下会出现-dependencypropertyunsetvalue&quot;&gt;什么情况下会出现 DependencyProperty.UnsetValue？&lt;/h2&gt;

&lt;p&gt;正常情况下，只有以下两处代码会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在用于绑定的转换器 &lt;code class=&quot;highlighter-rouge&quot;&gt;IValueConverter&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;IMultiValueConverter&lt;/code&gt; 的代码里面；&lt;/li&gt;
  &lt;li&gt;在 XAML 标记扩展 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 里面。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而以上两处代码，只有在发生以下三种情况时才会遇到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;绑定出现了错误，也就是说绑定从最开始的源值到目标值的若干次转换过程中任何阶段发生了错误以至于无法成功转换到目标值。&lt;br /&gt;
虽然我们写的是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;{Binding XXX}&lt;/code&gt;，但 &lt;code class=&quot;highlighter-rouge&quot;&gt;XXX&lt;/code&gt; 可能由另外的绑定来提供（例如逻辑父控件的 DataContext）。一次次绑定的源值是上一个绑定的目标值，于是这样的关系组合成一个绑定提供值的链条。链条中只要有一处不能提供合理的值，就会在绑定中得到 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsetValue&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;绑定或者标记扩展写在了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ControlTemplate&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt; 里面，但此时并没有指定数据源。&lt;br /&gt;
在模板应用到实际的控件之前，模板本身也会执行一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 的逻辑。于是如果绑定需要依赖于实际的控件，那么实际上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Binding&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;MarkupExtension&lt;/code&gt; 会至少执行两次，其中第一次便是模板中的那一次。此时获取依赖属性的值时拿到的便是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;使用依赖项属性的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadLocalValue&lt;/code&gt; 来获取值，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt;；但此时并没有为依赖对象设置值。&lt;br /&gt;
如果没有设置值，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetValue&lt;/code&gt; 会返回更低优先级的值，一般情况下是依赖项属性在注册时的默认值；但 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadLocalValue&lt;/code&gt; 就是在获取显式设置的那个值，如果没设，就只能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 了。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;我们应该如何正确使用-dependencypropertyunsetvalue&quot;&gt;我们应该如何正确使用 DependencyProperty.UnsetValue？&lt;/h2&gt;

&lt;p&gt;微软官方对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 的介绍，专门的文档中只有一个说法，就是用来表示“不合理”的值，却并没有说明什么情况下为合理，什么情况下为不合理。但好在微软将一些推荐写法散落在了多个不同的文章中。这里整理在一起，以便为大家对 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 的正确使用提供指导。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在注册依赖项属性的时候，不要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt; 作为默认值。&lt;br /&gt;
这个值本意其实并不是在说“未设置”，而是代表“不合理”。默认值必须是“合理地”才行。微软官方文档 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties?wt.mc_id=MVP&quot;&gt;Custom dependency properties&lt;/a&gt; 对此的解释是，如果默认值设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsetValue&lt;/code&gt;，则会在大家使用其值的时候产生混淆，并不能区分到底是依赖属性（的绑定系统）提供值的时候出错了还是因为只是默认没设置。&lt;/li&gt;
  &lt;li&gt;微软推荐在写绑定的转换器的时候，如果转换有错误，不应该抛出异常，而是应该返回一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;，以便阻止绑定中继续传递值。不过我认为错误应该更及时地被发现才能避免错误的继续蔓延，所以建议在 DEBUG 下依然抛出异常，而在发布的版本里返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsetValue&lt;/code&gt;。&lt;br /&gt;
微软的推荐出自于 &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth&quot;&gt;Data binding in depth&lt;/a&gt;，在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-convert-bound-data?wt.mc_id=MVP&quot;&gt;How to: Convert Bound Data&lt;/a&gt; 中给出了这种推荐的示例代码。&lt;/li&gt;
  &lt;li&gt;如果需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoerceValueCallback&lt;/code&gt; 回调中验证值的合理性，当值不合理的时候，返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyProperty.UnsetValue&lt;/code&gt;。&lt;br /&gt;
这将告诉依赖属性系统阻止这次值的更改。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth?wt.mc_id=MVP&quot;&gt;Data binding in depth - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-convert-bound-data?wt.mc_id=MVP&quot;&gt;How to: Convert Bound Data - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties?wt.mc_id=MVP&quot;&gt;Custom dependency properties - UWP app developer - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-callbacks-and-validation?wt.mc_id=MVP&quot;&gt;Dependency Property Callbacks and Validation - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/2811405/why-do-i-get-a-dependencyproperty-unsetvalue-when-converting-a-value-in-a-multib&quot;&gt;c# - Why do I get a DependencyProperty.UnsetValue when converting a value in a MultiBinding? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.unsetvalue(v=vs.110).aspx&quot;&gt;DependencyProperty.UnsetValue Field (System.Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/DependencyProperty.cs,ee7f3b3d5828e7ab&quot;&gt;UnsetValue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/xaml/how-to-use-dependencyproperty-unsetvalue.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/xaml/how-to-use-dependencyproperty-unsetvalue.html</guid>
        
        <category>DependencyProperty</category>
        
        <category>UnsetValue</category>
        
        <category>依赖属性</category>
        
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>用 AppContext 解决类库的更新兼容问题</title>
        <description>&lt;p&gt;还记得微软在 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/mitigation-pointer-based-touch-and-stylus-support?wt.mc_id=MVP&quot;&gt;Mitigation: Pointer-based Touch and Stylus Support&lt;/a&gt; 中告诉大家如何在 .NET Framework 4.7 中迁移 WPF 的触控到基于 Pointer 消息？记得关键的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;AppContextSwitchOverrides value=&quot;Switch.System.Windows.Input.Stylus.EnablePointerSupport=true&quot;/&amp;gt;&lt;/code&gt; 这一句吗？&lt;/p&gt;

&lt;p&gt;有没有好奇为何这一句话能用来控制微软基础类库中某一块功能的行为呢？阅读本文将了解微软为开发者提供的一套类库更新的兼容性解决方案——&lt;code class=&quot;highlighter-rouge&quot;&gt;AppContext&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;这是微软自 .NET Framework 4.6 开始为开发者们提供的方案。&lt;/p&gt;

&lt;p&gt;比如你打算为你的类库增加了一个功能——指定一个文件夹名称用于存放文件。你写出了这样的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 1.0 版本的类库&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StorageSomeInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 其他逻辑。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;故事背景&quot;&gt;故事背景&lt;/h2&gt;

&lt;p&gt;你将类库发布到 NuGet 上，一切运行安好。&lt;/p&gt;

&lt;p&gt;直到有一天，某人给 &lt;code class=&quot;highlighter-rouge&quot;&gt;directoryName&lt;/code&gt; 传入了空字符串。结果你的文件全部都不再存到指定的文件夹下，而是存到了根目录……这跟你的预期不符啊！&lt;/p&gt;

&lt;p&gt;然而，类库发布了这么久，这么多人都下载安装使用了，要是随随便便把代码改成这样，搞不好一大堆小伙伴将面临着崩溃……（谁知道他们有没有依赖于你的 BUG 编程呢？搞不好他们绞尽脑汁发现这样还可以存到根目录呢于是就开开心心地用了呢！）&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 2.0 版本的类库&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StorageSomeInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhitespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 其他逻辑。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[Obsolete]&lt;/code&gt; 是一个好方案，他能够指导开发者一步步迁移他们对 API 的使用。不过：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果调用的代码太多了，迁移起来就是个痛苦的差事儿。&lt;/li&gt;
  &lt;li&gt;难得取了个好名字，要知道取名字可是编程中最难的事儿之一啊！&lt;/li&gt;
  &lt;li&gt;更多的开发者们其实根本没意识到你写出了这个坑，于是凭什么让他们升级 API？！&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;使用-appcontext&quot;&gt;使用 AppContext&lt;/h2&gt;

&lt;p&gt;这时候祭出——&lt;code class=&quot;highlighter-rouge&quot;&gt;AppContext&lt;/code&gt;！&lt;/p&gt;

&lt;p&gt;将你的 2.0 代码改成这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 2.0 版本的类库&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StorageSomeInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetDirectoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directoryName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Switch.StorageSomeInfo.UseLegacyDirectoryName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 跑以前的代码&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 跑新的代码&lt;/span&gt;
         
        &lt;span class=&quot;c1&quot;&gt;// 其他逻辑。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么开发者们更新你的类库时，就有可以挽回的方案了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果开发者们没有遇到什么问题，那么恭喜你那位开发者很幸运没有踩到你的坑，你平滑迁移过去了！&lt;/li&gt;
  &lt;li&gt;如果开发者们遇到了根目录问题，那么你的更新日志中的指导说明将起作用。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你可以在更新日志中写下说明：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;建议开发者们修改此方法的调用，避免写出错误的代码；&lt;/li&gt;
    &lt;li&gt;如果开发者们很难改动这样的代码，可以要求开发者在 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 文件中添加以下代码以使用“遗弃的”逻辑。&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;  
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;runtime&amp;gt;&lt;/span&gt;  
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppContextSwitchOverrides&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Switch.StorageSomeInfo.UseLegacyDirectoryName=true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;   
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/runtime&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;更多-appcontext-的信息&quot;&gt;更多 AppContext 的信息&lt;/h2&gt;

&lt;p&gt;开发者们如果有多个开关需要开启或关闭，则使用分号分隔多个开关：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;AppContextSwitchOverrides&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;switchName1=value1;switchName2=value2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;开发者们如果不想写配置文件，也可以直接在程序中调用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;AppContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，甚至可以直接动用注册表：&lt;code class=&quot;highlighter-rouge&quot;&gt;HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext&lt;/code&gt; 作为 Key，字符串作为 Value。依然是分号分割的键值对作为注册表项的值来存。如果采用注册表方案，将影响这台计算机上运行的所有程序。&lt;/p&gt;

&lt;p&gt;这三种方式的优先级是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;代码中直接调用的优先级最高；&lt;/li&gt;
  &lt;li&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;app.config&lt;/code&gt; 中指定的优先级其次；&lt;/li&gt;
  &lt;li&gt;在注册表中指定的优先级最低。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;一点坑&quot;&gt;一点坑&lt;/h2&gt;

&lt;p&gt;在从 .NET Framework 4.6 升级到 4.7 后，注册表的方式貌似失效了。参考：&lt;a href=&quot;https://support.microsoft.com/en-us/help/4036977/fix-appcontext-switch-overrides-are-not-applied-to-applications-that-r&quot;&gt;FIX: AppContext switch overrides are not applied to applications that run on the .NET Framework 4.7&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet/2017/09/30/app-context.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet/2017/09/30/app-context.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>为 Visual Studio 使用通配符批量添加项目文件</title>
        <description>&lt;p&gt;通常大家都不会关心 Visual Studio 的项目文件里是如何记录这个项目所包含的所有文件的，因为各位开发者们早已经习惯于右键添加文件或者拖拽文件进项目了。但如果你在某一个文件夹中放了大量的文件（尤其是图片等资源文件），那么这时会卡很久才能拖进去，拖完之后如果还要批量修改生成操作，那真的是痛不欲生。&lt;/p&gt;

&lt;p&gt;但是，Visual Studio 提供的项目文件（*.csproj）其实是支持通配符的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;比如，我们通常的项目文件的片段是这样的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Default.rd.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\LockScreenLogo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\SplashScreen.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square150x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square44x44Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Square44x44Logo.targetsize-24_altform-unplated.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\StoreLogo.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\Wide310x150Logo.scale-200.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，改成这样的话，以后新添加的 &lt;code class=&quot;highlighter-rouge&quot;&gt;*.png&lt;/code&gt; 文件也会加入：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Default.rd.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\*.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而且，如果你想改生成方式，也很简单：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Default.rd.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\*.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，小心有坑，因为如果你的目录下是多个文件夹嵌套的话，需要用两个星号来表示可能出现多层文件夹：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Properties\Default.rd.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\*.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\**\*.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-select-the-files-to-build?wt.mc_id=MVP&quot;&gt;How to: Select the Files to Build - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/vs/2017/09/26/wildcards-in-vs-projects.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/vs/2017/09/26/wildcards-in-vs-projects.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>深入了解 WPF Dispatcher 的工作原理（Invoke/InvokeAsync 部分）</title>
        <description>&lt;p&gt;深耕 WPF 开发的各位程序员大大们一定避不开使用 Dispatcher。跨线程访问 UI 当然免不了用到它，将某个任务延迟到当前任务之后执行也会用到它。Dispatcher.Invoke、Dispatcher.BeginInvoke 是过去大家经常使用的方法，而 .NET Framework 4.5 中微软为我们带来了 Dispatcher.InvokeAsync 方法，它和前面两个有何不同？&lt;/p&gt;

&lt;p&gt;阅读本文将更深入地了解 Dispatcher 的工作机制。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是&lt;strong&gt;深入了解 WPF Dispatcher 的工作原理&lt;/strong&gt;系列文章的一部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;Invoke/InvokeAsync 部分&lt;/a&gt;（本文）&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;PushFrame 部分&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;回顾老旧的-begininvoke看看新的-invokeasync&quot;&gt;回顾老旧的 BeginInvoke，看看新的 InvokeAsync&lt;/h2&gt;

&lt;p&gt;微软自 .NET Framework 3.0 为我们引入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt; 方法就已存在。不过，看这名字的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Begin&lt;/code&gt; 前缀，有没有一种年代感？没错！这是微软在 .NET Framework 1.1 时代就推出的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Begin&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt; 异步编程模型（APM，&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm?wt.mc_id=MVP&quot;&gt;Asynchronous Programming Model&lt;/a&gt;）。虽说 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.BeginInvoke&lt;/code&gt; 并不完全按照 APM 模型来实现（毕竟没有对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;End&lt;/code&gt;，也没有返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;IAsyncResult&lt;/code&gt;），但这个类型毕竟也是做线程相关的事情，而且这个方法的签名明显还带着那个年代的影子。不止名字上带着 &lt;code class=&quot;highlighter-rouge&quot;&gt;Begin&lt;/code&gt; 表示异步的执行，而且参数列表中还存在着 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 这样古老的类型。要知道，现代化的方法可是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Func&lt;/code&gt; 加泛型啊！&lt;/p&gt;

&lt;p&gt;大家应该还对 .NET Framework 4.5 带给我们的重磅更新——&lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步模式感到兴奋，因为它让我们的异步代码变得跟同步代码一样写了。这是微软新推荐的异步编程模式，叫做 TAP（&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?wt.mc_id=MVP&quot;&gt;Task-based Asynchronous Pattern&lt;/a&gt;）。既然异步编程模式都换了，同为线程服务的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.BeginInvoke&lt;/code&gt; 怎能不改呢？于是，微软真的改了，就是从 .NET Framework 4.5 版本开始。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;它叫做——&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.InvokeAsync&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;begininvoke-和-invokeasync-有什么不同&quot;&gt;BeginInvoke 和 InvokeAsync 有什么不同？&lt;/h2&gt;

&lt;p&gt;这个还真得扒开微软的源码看一看呢！&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Browsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Browsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Browsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EditorBrowsable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorBrowsableState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一共五个重载，前面三个都被微软做了标记，让你在智能感知列表中看不见。（这里吐槽一下 ReSharper，明明微软已经不让显示了嘛，干嘛还把人家显示出来……）后面两个暂时还看得见，但那又如何？！根本没啥区别好吗！！！&lt;/p&gt;

&lt;p&gt;为什么会像上面那样吐槽，是因为我发现这五个不同的重载里面其实都调用了同一个内部方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LegacyBeginInvokeImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ValidatePriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;priority&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;InvokeAsyncImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里让我忍不住吐槽的是两点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Legacy&lt;/code&gt; 是个什么鬼！词典上说这是“遗产，老化的”意思啊！很明显这是近乎被微软遗弃的代码啊！&lt;/li&gt;
  &lt;li&gt;既然这五个重载都用了被遗弃的方法，为什么只有前面三个看不见，后面两个看得见啊！还有，微软你干嘛不标记为 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Obsolete]&lt;/code&gt; 呢！&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;好，吐槽结束。我们再来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 方法。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看吧，这才像微软新的 TAP 异步模式的代码啊。&lt;/p&gt;

&lt;p&gt;不带 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; 的四个重载会汇聚到带 &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; 的两个重载中，这两个重载代码除了泛型返回值以外几乎一模一样。所以我们拿第三个当研究对象看看：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;callback&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ValidatePriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;priority&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DispatcherOperation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;InvokeAsyncImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你发现了什么？这与那个被遗弃的 &lt;code class=&quot;highlighter-rouge&quot;&gt;LegacyBeginInvokeImpl&lt;/code&gt; 长得非常像。不，就是一模一样！你总不能说参数名称不同也要算吧……甚至……返回值类型也是一样的。&lt;/p&gt;

&lt;p&gt;既然这样，我们总算是明白微软到底在做些什么了。其实微软在 .NET Framework 4.5 中已经把 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt; 的实现改造成了 TAP 异步模式，但方法名字和老旧的参数列表却始终是微软的一块心病，于是痛下决心新增了 6 个更加现代的方法免得产生兼容性问题。不过由于里面的实现一模一样，并没有额外带来什么 BUG，所以微软也不好意思标记为 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Obsolete]&lt;/code&gt; 已过时了。&lt;/p&gt;

&lt;p&gt;既然两个方法一样，后文我也就没必要两个都说了，一切以新款的 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 为主。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;invokeasync-的实现原理&quot;&gt;InvokeAsync 的实现原理&lt;/h2&gt;

&lt;p&gt;前面一节几乎告诉我们，&lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 的关键就在 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsyncImpl&lt;/code&gt; 方法中。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcerOperation&lt;/code&gt; 把我们传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Func&lt;/code&gt; 包装起来。这样，我们传入的任务和优先级将在一起处理。&lt;/li&gt;
  &lt;li&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt; 加入到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;PriorityQueue&amp;lt;DispatcherOperation&amp;gt;&lt;/code&gt; 类型的队列中。这个队列内部实现是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;SortedList&lt;/code&gt;，于是每次入队之后，出队的时候一定是按照优先级出队的。&lt;/li&gt;
  &lt;li&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;RequestProcessing&lt;/code&gt;，直至最后向&lt;strong&gt;某个隐藏窗口&lt;/strong&gt;发送了一条消息。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;那个隐藏窗口&lt;/strong&gt;接收到了这条消息，然后从 &lt;code class=&quot;highlighter-rouge&quot;&gt;PriorityQueue&amp;lt;DispatcherOperation&amp;gt;&lt;/code&gt; 队列中取出一条任务执行（真实情况复杂一点，后面会谈到）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-26-01-20-05.png&quot; alt=&quot;InvokeAsync 的实现原理图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上面第 3 点的消息是这样发的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryPostMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_msgProcessQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等等，这句代码里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_window&lt;/code&gt; 是哪儿来的？为什么凭空出现了一个可以用来发送消息的窗口？于是，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 构造函数中发现了这个窗口。这并不是我们平时所熟知的那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 类，而是一个用于发送和接收 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 调度器调度任务消息的 Win32 隐藏窗口。不信它是一个窗口？请进入 &lt;code class=&quot;highlighter-rouge&quot;&gt;MessageOnlyHwndWrapper&lt;/code&gt; 类看，它的基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;HwndWrapper&lt;/code&gt; 中直接使用了方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsafeNativeMethods.CreateWindowEx&lt;/code&gt; 创建了这个窗口，然后拿到了它的句柄 &lt;code class=&quot;highlighter-rouge&quot;&gt;Handle&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;既然会向窗口发消息，自然而然可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;Hook&lt;/code&gt; 它的消息处理函数，就像下面这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Create the message-only window we use to receive messages&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// that tell us to process the queue.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MessageOnlyHwndWrapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MessageOnlyHwndWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityCriticalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MessageOnlyHwndWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;_hook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HwndWrapperHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WndProcHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而这里处理的消息类型只有三种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;关掉这个隐藏窗口；&lt;/li&gt;
  &lt;li&gt;处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 调度的任务（这个消息是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的静态构造函数中注册的）；&lt;/li&gt;
  &lt;li&gt;定时器。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;前面两个不难理解，但是为什么这里与定时器有关？！&lt;/p&gt;

&lt;p&gt;继续调查，我们发现微软在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 中把所有不同种类的优先级分成了三个大类：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前台优先级（对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Loaded&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Send&lt;/code&gt;，也就是数字 6~10）&lt;/li&gt;
  &lt;li&gt;后台优先级（对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Background&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Input&lt;/code&gt;，也就是数字 4~5）&lt;/li&gt;
  &lt;li&gt;空闲优先级（对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.SystemIdle&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.ApplicationIdle&lt;/code&gt;，也就是数字 1~3）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在这里微软又开始逗我们玩了……因为在他的处理中，后面两个是完全相同的！所以严格意义上只分了两种——前台优先级和非前台优先级。而区分他们的一个分界点就是——用户的输入。&lt;/p&gt;

&lt;p&gt;如果有用户的输入发生，那么会开启一个定时器，在定时器时间到达之前，所有的后台优先级任务都不会去执行。但前台优先级任务不受用户输入的影响。在这样的设定下，用户的输入不会随随便便被饿死，WPF 程序也就不会从输入层面开始卡顿了。&lt;/p&gt;

&lt;p&gt;研究到这里，似乎 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 的执行原理差不多都清楚了。但是不要忘了这可是 TAP 异步模式的一项实践啊，这方法是要支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 并附带返回值的。&lt;/p&gt;

&lt;p&gt;但这里就没有更多底层的内容了。我们注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 的返回值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt; 类型的，而这就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 方法中我们前面看到的代码中直接 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 出来的。&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt; 中有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 方法，这是无返回值的，但是它执行完后会置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; 值。另外 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt; 实现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAwaiter&lt;/code&gt; 方法，于是就可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;。对于如何自己实现一个可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 的类，我可能会专门写一篇文章，但如果你现在就希望了解，可以阅读：&lt;a href=&quot;https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/&quot;&gt;How to write a custom awaiter – Lucian’s VBlog&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;而被我们遗弃的 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt;，由于内部调用了同一个函数，所以实现原理是完全一样的。而且，这么古老的函数也允许 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;invoke-的实现原理&quot;&gt;Invoke 的实现原理&lt;/h2&gt;

&lt;p&gt;也许你会觉得奇怪。我们连“异步”的 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 的实现原理都了解了，同步的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 还有何难！&lt;/p&gt;

&lt;p&gt;如果你这么认为，你一定忽略了一个很重要的问题——死锁！&lt;/p&gt;

&lt;p&gt;如果是另一个线程调用到此线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt;，那么同步等待一下当然不会有问题。但是如果调用线程就是此线程本身呢？如果依然采用“同步等待”的方式，那么 UI 线程就会因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 的调用而阻塞，然而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 中传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt; 是插入到 UI 线程执行的，如果 UI 线程正在等待 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt;，还怎么插入得进去？！&lt;/p&gt;

&lt;p&gt;所以，它一定有另外一套实现方式！而微软为这套实现方式做了两条路径：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果是 10 的最高优先级，则直接调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 里传入的任务；&lt;/li&gt;
  &lt;li&gt;如果是其他，则调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait&lt;/code&gt; 方法进行等待。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;等等，这不还是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait&lt;/code&gt; 吗！然而进去 &lt;code class=&quot;highlighter-rouge&quot;&gt;Wait&lt;/code&gt; 方法查看，你会发现，根本不是！&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherOperationStatus&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略一些前面的代码。&lt;/span&gt;
            
    &lt;span class=&quot;c1&quot;&gt;// We are the dispatching thread for this operation, so&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// we can't block.  We will push a frame instead.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DispatcherOperationFrame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherOperationFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
    &lt;span class=&quot;c1&quot;&gt;// 省略一些后面的代码。&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt;。这样保证了在不阻塞线程的情况下进行“等待”。至于如何做到“不阻塞地等待”，请参阅本系列的第二篇文章 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;进入了 .NET Framework 4.5 及以上的开发者们，建议使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 代替 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeginInvoke&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 通过创建一个隐藏的消息窗口来让一个个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 到此线程的任务按照优先级执行；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 做到了不阻塞 UI 线程的等待。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;异步编程模型
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm?wt.mc_id=MVP&quot;&gt;Asynchronous Programming Model (APM) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/aa719595(v=vs.71).aspx&quot;&gt;Asynchronous Design Pattern Overview&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types?wt.mc_id=MVP&quot;&gt;Interop with Other Asynchronous Patterns and Types - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?wt.mc_id=MVP&quot;&gt;Task-based Asynchronous Pattern (TAP) - Microsoft Docs&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;InvokeAsync
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs&quot;&gt;Dispatcher.cs&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;WPF 消息机制
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/powertoolsteam/article/details/6109036&quot;&gt;WPF的消息机制（二）- WPF内部的5个窗口之隐藏消息窗口 - 葡萄城控件技术团队博客 - CSDN博客&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Awaiter
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/&quot;&gt;How to write a custom awaiter – Lucian’s VBlog&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Dec 2018 01:54:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet/2017/09/26/dispatcher-invoke-async.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet/2017/09/26/dispatcher-invoke-async.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>git 如何更可靠地解决冲突？</title>
        <description>&lt;p&gt;使用 git 合并代码时出现冲突是很常见的，不过如何解冲突才能更加可靠呢？不漏掉别人的修改，也同时让自己的修改完全保留。&lt;/p&gt;

&lt;p&gt;本文将介绍利用各种工具更可靠地解决冲突。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-visual-studio&quot;&gt;使用 Visual Studio&lt;/h2&gt;

&lt;p&gt;如果你使用 Visual Studio，那么当合并两个分支出现冲突的时候，Visual Studio 的 Team Explorer 会显示当前冲突的所有文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-09-19-14-48.png&quot; alt=&quot;Visual Studio 中的冲突文件&quot; /&gt;&lt;br /&gt;
▲ 图 1&lt;/p&gt;

&lt;p&gt;当你点击其中一个文件的 Merge 按钮的时候，会显示这个文件的合并界面。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-09-19-21-18.png&quot; alt=&quot;合并单个文件&quot; /&gt;&lt;br /&gt;
▲ 图 2&lt;/p&gt;

&lt;p&gt;可以看到，上图中两边的差异非常大。左边的代码进行了某项不为人知的修改，而右边的这些代码都被删除了。如果采用左边，则右边删除移走的代码就会留存，形成两份相似的代码；如果采用右边，那么左边在另一个分支的修改就会丢失。&lt;/p&gt;

&lt;p&gt;这时，可以 &lt;strong&gt;和基准进行比较&lt;/strong&gt; 来查看两个分支相对于同样的基准提交的差异来手动解决冲突。&lt;/p&gt;

&lt;p&gt;点击合并页面顶部工具栏中的选择比较源按钮，可以切换比较代码的双方。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-09-19-37-43.png&quot; alt=&quot;切换比较代码的双方&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你可以比较“源”和“目标”，这也是默认的比较。你还可以选择比较“源”和“基准”、“目标”和“基准”。这样，你可以分别查看两边代码相比于同一个基准提交的修改。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-09-19-43-28.png&quot; alt=&quot;查看源与基准的修改&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于左边的修改，通过查看“源”与“基准”的不同，得知左边其实值修改了一行。那么这时，我们把这一行重新补充到右边删除移走的代码里面（被移到了另一个文件中，需要补充到那个文件中对应的地方），那么两边的修改才真正时等价合并了。&lt;/p&gt;

&lt;p&gt;你可以在下图看到这两个不同选项的理解。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-09-19-37-14.png&quot; alt=&quot;git 合并过程&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Dec 2018 11:45:25 +0000</pubDate>
        <link>https://blog.walterlv.com/post/resolve-git-conflicts.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/resolve-git-conflicts.html</guid>
        
        
        <category>git</category>
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>使用 ReSharper，输入即遵循 StyleCop 的代码格式化规范</title>
        <description>&lt;p&gt;StyleCop 可以帮助强制执行代码格式化规范，ReSharper 可以帮助你更高效地编写代码。把两者结合起来，你便能高效地编写符合团队强制格式化规范的代码来。&lt;/p&gt;

&lt;p&gt;本文就介绍如何使用 ReSharper 来高效地遵循 StyleCop 的代码格式化规范。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装插件-stylecop-by-jetbrains&quot;&gt;安装插件 StyleCop by JetBrains&lt;/h2&gt;

&lt;p&gt;StyleCop by JetBrains 插件的开发名称是 StyleCop.ReSharper，所以你也可以通过搜索 StyleCop.ReSharper 得到同样的插件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-09-42-41.png&quot; alt=&quot;StyleCop by JetBrains&quot; /&gt;&lt;br /&gt;
▲ StyleCop by JetBrains 的图标&lt;/p&gt;

&lt;p&gt;先安装 &lt;a href=&quot;https://resharper-plugins.jetbrains.com/packages/StyleCop.StyleCop/&quot;&gt;StyleCop by JetBrains&lt;/a&gt; 插件。注意这是 ReSharper 的插件，而不是 Visual Studio 的插件。你需要到 ReSharper 的 Extension Manager 中去下载。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-09-23-40.png&quot; alt=&quot;前往 ReSharper 的 Extension Manager&quot; /&gt;&lt;br /&gt;
▲ 前往 ReSharper 的 Extension Manager&lt;/p&gt;

&lt;p&gt;在 ReSharper 自己的插件管理页面，搜索并安装 StyleCop by JetBrains 插件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-09-35-52.png&quot; alt=&quot;搜索并安装 StyleCop by JetBrains&quot; /&gt;&lt;br /&gt;
▲ 搜索并安装 StyleCop by JetBrains&lt;/p&gt;

&lt;p&gt;当你点击了窗口下面的那个“Install”按钮后，ReSharper 会弹出一个等待窗口一次性安装完毕。你需要等待，等待的时间取决于网速。&lt;/p&gt;

&lt;p&gt;安装完之后，重启 Visual Studio 就会生效。如果你稍后见到了本节上面的图标，那么那实际上就是 StyleCop by JetBrains 插件的一部分。&lt;/p&gt;

&lt;h2 id=&quot;修改-stylecop-by-jetbrains-的规则&quot;&gt;修改 StyleCop by JetBrains 的规则&lt;/h2&gt;

&lt;p&gt;现在打开一个以前写的项目，你可能会发现大量的代码都已被波浪线入侵 😭 。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-10-12-10.png&quot; alt=&quot;代码已被波浪线入侵&quot; /&gt;&lt;br /&gt;
▲ 代码已被波浪线入侵，代码源自我的另一篇博客：&lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;如何实现一个可以用 await 异步等待的 Awaiter&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如果你现在编写新的代码，你会发现新的代码已经开始使用 StyleCop 建议的规则了。不过，可能这个规则并不是你希望的规则，正如这张图所描述的那样：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The documentation text within the param tag does not contain any whitespace between words, indicating that it most likely does not follow a proper grammatical structure required for documentation text. [StyleCp Rule: SA1630]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;翻译过来：&lt;code class=&quot;highlighter-rouge&quot;&gt;param&lt;/code&gt; 标记中的文档文本不包含任何单词之间的空格，表示它很可能不遵循文档文本所需的正确语法结构。&lt;code class=&quot;highlighter-rouge&quot;&gt;[StyleCp规则：SA1630]&lt;/code&gt;。很明显，这一条 StyleCop 规则连中文都没有考虑过，中文文本怎么可能包含单词之间的空格呢 😂 。&lt;/p&gt;

&lt;p&gt;所以，很明显我们需要定制我们自己的 StyleCop 规则。&lt;/p&gt;

&lt;p&gt;在 ReSharper 的设置中找到 Code Inspection -&amp;gt; Inspection Serverity -&amp;gt; C# -&amp;gt; StyleCop。展开之后你就能看到 StyleCop by JetBrains 的规则定制了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-11-52-48.png&quot; alt=&quot;定制规则&quot; /&gt;&lt;br /&gt;
▲ 定制规则&lt;/p&gt;

&lt;p&gt;在这里，按照你的团队约定，将一项项的值设置为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不遵守&lt;/li&gt;
  &lt;li&gt;提示&lt;/li&gt;
  &lt;li&gt;建议&lt;/li&gt;
  &lt;li&gt;警告&lt;/li&gt;
  &lt;li&gt;错误&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-12-01-12-06-37.png&quot; alt=&quot;设置团队约定&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;不通用的-stylecop-by-jetbrains-规则&quot;&gt;不通用的 StyleCop by JetBrains 规则&lt;/h2&gt;

&lt;p&gt;实际上使用此插件生成的 StyleCop 规则并不是 StyleCop 的通用配置，而是生成了一个 DotSettings 的 ReSharper 配置。&lt;/p&gt;

&lt;p&gt;如果需要使用到通用配置，请阅读 &lt;a href=&quot;/post/introduce-stylecop-into-teams&quot;&gt;在 Visual Studio 中使用 StyleCop 来约束团队代码规范&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.jetbrains.com/dotnet/2018/04/09/stylecop-code-style-settings-inspections/&quot;&gt;StyleCop code style settings and inspections - .NET Tools Blog.NET Tools Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://resharper-plugins.jetbrains.com/packages/StyleCop.StyleCop/&quot;&gt;ReSharper Gallery - StyleCop by JetBrains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Dec 2018 04:36:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-code-with-stylecop-using-resharper.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-code-with-stylecop-using-resharper.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>用 dotTrace 进行性能分析时，各种不同性能分析选项的含义和用途</title>
        <description>&lt;p&gt;对 .NET 程序进行性能分析，dotTrace 能应对绝大多数的场景。在开启一个进程进行性能分析之前，我们会看到一些性能分析选项（Profiler Options）。本文将介绍这几个选项的含义，并用实际的例子来说明其用途。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;dottrace-的性能分析选项&quot;&gt;dotTrace 的性能分析选项&lt;/h2&gt;

&lt;p&gt;你可以前往 &lt;a href=&quot;https://www.jetbrains.com/profiler/download/&quot;&gt;Download dotTrace: .NET Performance Profiler by JetBrains&lt;/a&gt; 下载 dotTrace。&lt;/p&gt;

&lt;p&gt;本文要说的就是下图右边的那四个选项，在启动一个进程进行性能分析之前可以看得见的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-15-29-04.png&quot; alt=&quot;dotTrace 启动进程进行性能分析的界面&quot; /&gt;&lt;br /&gt;
▲ dotTrace 启动进程进行性能分析的界面&lt;/p&gt;

&lt;p&gt;有四个选项：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sampling&lt;/li&gt;
  &lt;li&gt;Tracing&lt;/li&gt;
  &lt;li&gt;Line-by-Line&lt;/li&gt;
  &lt;li&gt;Timeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sampling-采样&quot;&gt;Sampling 采样&lt;/h2&gt;

&lt;p&gt;界面中的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Accurate measurement of call time. Optimal for most use cases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;使用此选项进行启动进程后，会准确测量不同方法的执行时间，但不会统计方法的调用次数。&lt;/p&gt;

&lt;p&gt;这适用于大多数场景。尤其是如果你还没有对你的程序进行过任何性能分析的情况下，先使用这个选项进行一个初步分析大致确定性能问题是很方便的。&lt;/p&gt;

&lt;h2 id=&quot;tracing-追踪&quot;&gt;Tracing 追踪&lt;/h2&gt;

&lt;p&gt;界面中的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Accurate measurement of calls number. Optimal for analyzing algorithm complexity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;会准确地测量调用次数。但在此选项下，时间的测量将由于性能分析的开销过大而可能不准确。&lt;/p&gt;

&lt;p&gt;如果你使用 Sampling 分析方式得不到你想要的性能分析数据的时候，你可能用得到此选项。例如，当你分析算法复杂度，需要明确知道方法的调用次数，而不需要知道方法的准确执行时间的时候。&lt;/p&gt;

&lt;h2 id=&quot;line-by-line-逐行&quot;&gt;Line-by-line 逐行&lt;/h2&gt;

&lt;p&gt;界面中只写了一句根本无法理解的话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Advanced use cases only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;仅仅说了这是高级使用场景，名没有说什么样的场景。&lt;/p&gt;

&lt;p&gt;这个选项下，分析器会测量每行代码。由于性能分析的开销过于巨大，调用时间的测量也是不准确的。如果要降低此选项下的开销，你可以使用过滤器仅分析特定的方法。关于使用过滤器，可以阅读官方文档 &lt;a href=&quot;https://www.jetbrains.com/help/profiler/Profiler_Options.html#filters&quot;&gt;Profiler Options - Help - dotTrace&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;当你已经通过其他方法得知性能问题出现在哪个具体的方法时你可能需要用到这个选项，这会分析此方法的每一行代码。&lt;/p&gt;

&lt;h2 id=&quot;timeline-时间线&quot;&gt;Timeline 时间线&lt;/h2&gt;

&lt;p&gt;界面中的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Measurement of temporal performance data. Optimal for most use cases including analysis of multi-threaded applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;收集有关线程状态、应用程序事件和其他多线程数据的时态数据。此方法基于 Windows 的事件跟踪器（ETW）。&lt;/p&gt;

&lt;p&gt;推荐用于大多数情况，尤其是分析多线程应用程序的时候。你可以用这个选项来确定 UI 卡顿或不响应的原因，可以分析过多的 GC（垃圾回收），可以分析不均匀的工作负载分配、IO 不足或者其他各种异常。&lt;/p&gt;

&lt;p&gt;由于需要用到 Windows 的事件跟踪器（ETW），所以你可能遭遇 ETW 相关的问题。具体可以阅读 &lt;a href=&quot;/post/dottrace-timeline-not-working&quot;&gt;用 dotTrace 进行性能分析时，Timeline 打不开？无法启动进程？也许你需要先开启系统性能计数器的访问权限&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/help/profiler/Profiler_Options.html&quot;&gt;Profiler Options - Help - dotTrace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 28 Nov 2018 08:25:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dottrace-profiler-options.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dottrace-profiler-options.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Win2D 中的游戏循环：CanvasAnimatedControl</title>
        <description>&lt;p&gt;Win2D 是 DirectX 的一个高层封装，提供了极大 DirectX 性能的同时，又具有很好用的 API 设计。&lt;/p&gt;

&lt;p&gt;用 Win2D 除了能做出高性能的视觉效果之外，还可以轻而易举地搭建一个游戏循环出来。使用 Win2D 的游戏循环，你可以直接做出一个简单的游戏出来。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-win2d-做出来的游戏&quot;&gt;使用 Win2D 做出来的游戏&lt;/h2&gt;

&lt;p&gt;我在 GitHub 上开源了我正在做的一个基于 Win2D 的小游戏 —— GravityMaze，可以翻译为重力迷宫。本意是使用手机的重力感应器借助于自然重力的方式玩这款游戏，不过考虑到 Windows 10 Mobile 的手机太少，用户数量太少，其实我还是直接展示 UWP 桌面版好了。使用方向键可以控制桌面的倾斜角度，以便间接控制小球的运动方向。&lt;/p&gt;

&lt;p&gt;当然，我自己是有一部 Lumia 950XL 的，你可以在 &lt;a href=&quot;/post/uwp-accelerometer&quot;&gt;使用 Windows 10 中的加速度计（Accelerometer，重力传感器）&lt;/a&gt; 一文中看到它的身影。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-walterlv-gravity-maze.gif&quot; alt=&quot;重力迷宫&quot; /&gt;&lt;br /&gt;
▲ 重力迷宫&lt;/p&gt;

&lt;p&gt;这张图的红色背景是我自己拍摄的，所以绝不可能存在版权问题。&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;要使用 Win2D 进行简单的游戏开发，你需要先配置好一些 UWP 的开发环境，并且在你的项目中安装 Win2D.uwp 的 NuGet 包。阅读 &lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-win2d-%E5%85%A5%E9%97%A8-%E7%9C%8B%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86.html&quot;&gt;win10 uwp win2d 入门 看这一篇就够了 - 林德熙&lt;/a&gt; 了解如何在你的项目中安装 Win2D，并且了解 Win2D 基本的知识。&lt;/p&gt;

&lt;h2 id=&quot;win2d-中的画布控件&quot;&gt;Win2D 中的画布控件&lt;/h2&gt;

&lt;p&gt;Win2D 中的画布有 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasControl&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasVirtualControl&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasControl&lt;/code&gt; 用于进行一次性绘制，或者那些不常更新的画面内容。例如进行软件的 UI 绘制，或者软件中所得图形的绘制。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasVirtualControl&lt;/code&gt; 适用于在一个很大的画面中，只显示一个小部分的情况。例如显示大地图的一部分，或者显示大量超界的笔迹内容。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt; 适用于显示频繁更新的画面。典型的例子就是游戏。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;canvasanimatedcontrol&quot;&gt;CanvasAnimatedControl&lt;/h2&gt;

&lt;p&gt;我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt; 来做游戏循环，因为这是 Win2D 这几个控件中最适合做游戏循环的控件了。&lt;/p&gt;

&lt;p&gt;要在你的项目中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt;，你需要在 XAML 中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;using:Microsoft.Graphics.Canvas.UI.Xaml&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.GravityMaze.Pages.GamePage&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:xaml=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;using:Microsoft.Graphics.Canvas.UI.Xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;xaml:CanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OnUpdate&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Draw=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OnDraw&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，我们订阅 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt; 的两个事件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用于更新游戏中的数据，更新参考的是游戏时间线。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用于绘制游戏的内容。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是游戏循环最必要的两个事件了，其他虽然也是需要的，但也可以不写。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MazeGame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasAnimatedUpdateEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 根据时间线更新游戏数据。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasAnimatedDrawEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 绘制游戏画面。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DrawingSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;canvasanimatedcontrol-在游戏中的使用&quot;&gt;CanvasAnimatedControl 在游戏中的使用&lt;/h2&gt;

&lt;p&gt;你在我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GamePage&lt;/code&gt; 中其实看不到对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 事件的实际使用，因为我把它们都封装到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MazeGame&lt;/code&gt; 中了。&lt;/p&gt;

&lt;p&gt;有些信息需要注意：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 运行于相同的线程，但都不是主线程；所以你不可以从这里去获取主线程中的 UI 资源。&lt;/li&gt;
  &lt;li&gt;正常情况下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 调用一次之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 就会调用一次；但如果当前运行缓慢，那么多次 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 调用之后才会调用一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;如果 UWP 窗口最小化了，那么只会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 方法，而不会调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-20-14-26.png&quot; alt=&quot;线程&quot; /&gt;&lt;br /&gt;
▲ 线程&lt;/p&gt;

&lt;p&gt;在 GravityMaze 重力迷宫中，主要是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Player&lt;/code&gt; 也就是你在上面动图中看到的那个小球需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 中更新数据，其他其实只需要画就好了。&lt;code class=&quot;highlighter-rouge&quot;&gt;Update&lt;/code&gt; 中我需要计算速度、加速度以及进行碰撞检测。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasAnimatedUpdateEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ElapsedTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TotalSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// 1. 根据重力感应器或者键盘计算这一帧桌面的倾斜角度。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 2. 计算这一倾角带来的加速度。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 3. 计算是否跌入黑洞。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 4. 将加速度叠加阻力。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 5. 计算此速度和加速度下的位置。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 6. 进行边缘检测和碰撞检测。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 中，只绘制了那个球：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasAnimatedDrawEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 绘制游戏画面。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DrawingSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FillEllipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_xPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_yPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;事实上你在上面动图看到的球并不是一个毫无生机的灰球，而是一个具有特效的半透明塑料弹球。你可以阅读 &lt;a href=&quot;/post/draw-ellipse-with-bitmap-texture-using-win2d&quot;&gt;使用 Win2D 绘制带图片纹理的圆（或椭圆）&lt;/a&gt; 了解如何绘制这样的塑料弹球。&lt;/p&gt;

&lt;h2 id=&quot;canvasanimatedcontrol-中-createresources-事件&quot;&gt;CanvasAnimatedControl 中 CreateResources 事件&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasAnimatedControl&lt;/code&gt; 中还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateResources&lt;/code&gt; 事件，对更复杂的游戏循环有所帮助。当需要创建资源的时候会引发此事件。&lt;/p&gt;

&lt;p&gt;第一次使用的时候就需要创建资源；除此之外，如果设备丢失，也需要创建资源。阅读 &lt;a href=&quot;https://www.cnblogs.com/validvoid/p/win2d-handling-device-lost.html&quot;&gt;Win2D 官方文章系列翻译 - 处理设备丢失 - void² - 博客园&lt;/a&gt; 了解更多关于设备丢失的内容。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasBitmap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_boardMaterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCreateResources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasCreateResourcesEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 其中，GameCanvas 是 XAML 中 CanvasAnimatedControl 的名称。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_boardMaterial&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ms-appx:///Assets/Game/Boards/table.jpg}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_boardMaterial&lt;/code&gt; 就是你在上面动图中看到的后面那张红色背景。&lt;/p&gt;

&lt;p&gt;这样，便可以在需要的时候创建资源。&lt;/p&gt;

&lt;p&gt;不过，这时你需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Draw&lt;/code&gt; 中先判空再绘制。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasAnimatedDrawEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DrawingSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 其中，FullBounds 是 Rect 类型，我在 Page 的 SizeChanged 中给它赋的值。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_boardMaterial&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_boardMaterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FillRectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你也可以使用事件参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasCreateResourcesEventArgs&lt;/code&gt; 来追踪这个异步加载任务，这样能够在绘制之前确保资源被加载完毕。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCreateResources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanvasAnimatedControl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasCreateResourcesEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TrackAsyncAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateResourcesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsAsyncAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateResourcesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_boardMaterial&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CanvasBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ms-appx:///Assets/Game/Boards/table.jpg}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-win2d-%E5%85%A5%E9%97%A8-%E7%9C%8B%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86.html&quot;&gt;win10 uwp win2d 入门 看这一篇就够了 - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-win2d-CanvasVirtualControl-%E4%B8%8E-CanvasAnimatedControl.html&quot;&gt;win10 uwp win2d CanvasVirtualControl 与 CanvasAnimatedControl - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/win10-uwp-%E8%90%A4%E7%81%AB%E8%99%AB%E6%95%88%E6%9E%9C.html&quot;&gt;win10 uwp 萤火虫效果 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 28 Nov 2018 08:25:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/game-loop-of-win2d-canvas-animated-control.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/game-loop-of-win2d-canvas-animated-control.html</guid>
        
        
        <category>dotnet</category>
        
        <category>win2d</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用 Win2D 绘制带图片纹理的圆（或椭圆）</title>
        <description>&lt;p&gt;使用 Win2D 绘制图片和绘制椭圆都非常容易，可是如何使用 Win2D 绘制图片纹理的椭圆呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;重力迷宫小球&quot;&gt;重力迷宫小球&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-walterlv-gravity-maze.gif&quot; alt=&quot;重力迷宫&quot; /&gt;&lt;br /&gt;
▲ 重力迷宫&lt;/p&gt;

&lt;p&gt;你可以看到这个小球就像一个透明塑料小球一样，纹理会跟随背景而动。这显然不是 &lt;a href=&quot;/post/game-loop-of-win2d-canvas-animated-control&quot;&gt;Win2D 中的游戏循环：CanvasAnimatedControl&lt;/a&gt; 一文中我用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DrawEllipse&lt;/code&gt; 画的那个灰色小球。&lt;/p&gt;

&lt;h2 id=&quot;win2d-实现&quot;&gt;Win2D 实现&lt;/h2&gt;

&lt;p&gt;我们会使用到 Win2D 中的多种特效：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MorphologyEffect&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;用于将背景那些红色的洞洞转换成较虚的形态，以便球看起来不是扁平的。&lt;/li&gt;
      &lt;li&gt;不是必要的，只是为了好看而已。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;CropEffect&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;将背景区域裁剪成一个较小的区域。&lt;/li&gt;
      &lt;li&gt;不是必要的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AlphaMaskEffect&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;使用透明度蒙版使得图片只露出椭圆部分。&lt;/li&gt;
      &lt;li&gt;这是绘制椭圆必要的特效。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ShadowEffect&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;做一个小球的阴影。&lt;/li&gt;
      &lt;li&gt;不是必要的。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要画出图片纹理的椭圆，只需要这么一点代码即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CanvasCommandList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDrawingSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FillEllipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_xPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_yPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AlphaMaskEffect&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AlphaMask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-11-21-44-05.png&quot; alt=&quot;带图片纹理的椭圆&quot; /&gt;&lt;br /&gt;
▲ 带图片纹理的椭圆&lt;/p&gt;

&lt;p&gt;现在，如果你希望获得本文一开始获得的那种奇妙的效果，可以添加更多的特效：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Material&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;morphology&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MorphologyEffect&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MorphologyEffectMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dilate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CropEffect&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;morphology&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SourceRectangle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_xPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_yPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CanvasCommandList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDrawingSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FillEllipse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_xPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_yPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AlphaMaskEffect&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AlphaMask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shadow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ShadowEffect&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BlurAmount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ShadowColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromArgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0x40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于-canvascommandlist&quot;&gt;关于 CanvasCommandList&lt;/h2&gt;

&lt;p&gt;上面的例子中，我们是用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasCommandList&lt;/code&gt;。它可以帮助我们将绘制命令先绘制到一个缓存的上下文中，以便被其他绘制上下文进行统一的处理。&lt;/p&gt;

&lt;p&gt;阅读林德熙的博客了解更多 &lt;code class=&quot;highlighter-rouge&quot;&gt;CanvasCommandList&lt;/code&gt; 的资料：&lt;a href=&quot;https://blog.lindexi.com/post/win2d-CanvasCommandList-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95.html&quot;&gt;win2d CanvasCommandList 使用方法 - 林德熙&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Wed, 28 Nov 2018 08:25:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/draw-bitmap-ellipse-using-win2d.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/draw-bitmap-ellipse-using-win2d.html</guid>
        
        
        <category>dotnet</category>
        
        <category>win2d</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>如何在单元测试中使用 Dispatcher.Invoke/InvokeAsync？</title>
        <description>&lt;p&gt;对于部分涉及到 WPF UI 的部分，单元测试一般都难以进行。但是，如果只是使用到其中的 UI 线程调度，那就稍微容易一些。不过为了找到这个方法我做了很多天的尝试。&lt;/p&gt;

&lt;p&gt;本文将提供一种在单元测试中运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 的方法，以便能够在单元测试中测试到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke/InvokeAsync&lt;/code&gt; 是否按要求执行。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我第一个想到的是在当前函数中执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Run&lt;/code&gt;，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 之后就阻塞了，我还怎么测试呢？&lt;/p&gt;

&lt;p&gt;于是我又想到我上个月写的辅助方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher.RunNewAsync()&lt;/code&gt;，在后台创建一个运行起来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;。参见我博客 &lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;如何实现一个可以用 await 异步等待的 Awaiter - walterlv&lt;/a&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher&lt;/code&gt; 的实现。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-07-20-49-51.png&quot; alt=&quot;UIDispatcher&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这方法确实可行，可以 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;。然而单元测试中只有一个单元测试可以通过，无论什么测试，只有第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 起来的可以通过，其它的全部无法完成（已知运行中，无法退出单元测试）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;最后，在 &lt;a href=&quot;https://stackoverflow.com/questions/1106881/using-the-wpf-dispatcher-in-unit-tests&quot;&gt;c# - Using the WPF Dispatcher in unit tests - Stack Overflow&lt;/a&gt; 发现其实可以先 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 再 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt;，这样，即便是当前的单元测试线程也是可以正常完成的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunInDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个方法借鉴了此前我和我朋友研究过的 WPF DoEvents（虽然已被弃用）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/wpf-DoEvents.html&quot;&gt;wpf DoEvents - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;于是，单元测试可以这样做：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestSomething_SomethingHappened&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;RunInDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 做一些事情。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 然后……&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 然后干些啥……&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 然后等待 Measure/Arrange。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 然后再验证值。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Yield&lt;/code&gt; 的意思可以参见我的另一篇博客 &lt;a href=&quot;/post/yield-in-task-dispatcher&quot;&gt;出让执行权：Task.Yield, Dispathcer.Yield - walterlv&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;以上。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1106881/using-the-wpf-dispatcher-in-unit-tests&quot;&gt;c# - Using the WPF Dispatcher in unit tests - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Nov 2018 05:10:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/run-dispatcher-in-unit-test.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/run-dispatcher-in-unit-test.html</guid>
        
        
        <category>wpf</category>
        
        <category>unittest</category>
        
      </item>
    
      <item>
        <title>技术、产品、交流、思考 - 微软技术暨生态大会 2018</title>
        <description>&lt;p&gt;微软技术暨生态大会 2018（Microsoft Tech Summit 2018）已于 2018 年 10 月 25-27 日在上海世博中心召开。本文记录我们参会后的一些知识和思考。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;路&quot;&gt;路&lt;/h2&gt;

&lt;p&gt;2018 年 10 月 23 日中午，我和&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt;踏上了前往上海的旅程。这是德熙第一次进行如此长途的旅行，之前几乎一直待在家里。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-28-11.png&quot; alt=&quot;微软技术暨生态大会 - 上海世博中心&quot; /&gt;&lt;/p&gt;

&lt;p&gt;微软技术暨生态大会是从 10 月 25 日到 10 月 27 日，而 10 月 24 日是微软合作伙伴会议。我们当初在制定行程时，并没有意识到微软合作伙伴会议与微软技术暨生态大会是分开的，所以我们其实多准备了一天的行程。不过这也正好可以有额外充足的时间了解世博中心和微软合作伙伴展示的产品。&lt;/p&gt;

&lt;p&gt;我们是乘坐高铁往返的，平均单程 8 个小时。这是非常久的时间，除了高铁噪声之外很安静。我们可以做非常多的阅读、思考、交流、准备。当然，我还利用这样的时间修改分会场课程的课件，然后预演，为课程 &lt;a href=&quot;/post/dotnet-build-and-roslyn-course-in-tech-summit-2018&quot;&gt;预编译框架 - 开发高性能应用&lt;/a&gt; 做准备。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-29-34.png&quot; alt=&quot;高铁&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;产品&quot;&gt;产品&lt;/h2&gt;

&lt;p&gt;产品展厅自 24 日起就开始展示。合作商覆盖微软推广的各个方向 —— 云、人工智能、DevOps 开发者套件、现代化的工作模式……作为开发者，由于这部分的合作涉及到较多商务上的沟通，所以我们并没有进行深入的交流。&lt;/p&gt;

&lt;h2 id=&quot;吃&quot;&gt;吃&lt;/h2&gt;

&lt;p&gt;现场为所有的参会人员准备了午餐盒饭，在 &lt;a href=&quot;https://www.ithome.com/0/390/988.htm&quot;&gt;微软粉丝之夜：IT之家读者捕捉铺路集团董事长玄隐 - 微软,粉丝之夜,技术暨生态大会 - IT之家&lt;/a&gt; 中你能找到 &lt;a href=&quot;https://blog.miniasp.com/&quot;&gt;MVP 保哥&lt;/a&gt;和 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;MVP 林德熙&lt;/a&gt;的午餐图片。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-31-05.png&quot; alt=&quot;现场的午餐&quot; /&gt;&lt;/p&gt;

&lt;p&gt;只有我一个人觉得这份午餐的分量很足还能吃剩吗？为什么大家都说吃不饱？&lt;/p&gt;

&lt;p&gt;额外的，在粉丝之夜有简餐，是一些西餐甜点，都是凉的。由于我来之前的几天闹肚子，所以虽然饿但只吃了一点点。而林德熙也饿，吃了很多，结果第二天开始闹肚子😂。&lt;/p&gt;

&lt;p&gt;所以我们竟然四个晚上都光顾了附近外卖的一家粥铺。&lt;/p&gt;

&lt;h2 id=&quot;技术&quot;&gt;技术&lt;/h2&gt;

&lt;h3 id=&quot;visual-studio-协同开发&quot;&gt;Visual Studio 协同开发&lt;/h3&gt;

&lt;p&gt;第一天整天都不像是一个技术交流，不过这一切在 26 号起有了变化。由于我早上去分会场进行 27 号分会场课程彩排，所以错过了一开始的主会场内容。&lt;/p&gt;

&lt;p&gt;据小伙伴林德熙的描述，第一个是令人振奋的是 Visual Studio 的协作功能。在现场演示中，工程师通过分享一个 Visual Studio 的协同编辑链接，另一位工程师可以协同编写前者的代码。在此过程中，编写和调试都和在另一台电脑上无异。&lt;/p&gt;

&lt;p&gt;这无疑是团队进行开发协作的一项重大功能！结对编程在软件工程中不止是共同完成某项功能，减少 Bug，更能让不同的开发者之间互相学习对方的技术、开发手法以及以及高效习惯。而 Visual Studio 的协同开发功能无疑让结对编程的成本突然间减小了许多。&lt;/p&gt;

&lt;p&gt;当然，不止是协同编辑，还能协同调试。不止在 Visual Studio 中能够使用，在 Visual Studio Code 中也一样可以使用。&lt;/p&gt;

&lt;h3 id=&quot;appcenter-在一处管理你所有的应用发布&quot;&gt;AppCenter 在一处管理你所有的应用发布&lt;/h3&gt;

&lt;p&gt;接下来是开源与跨平台相关的技术演讲。&lt;/p&gt;

&lt;p&gt;在介绍 Xamarin 的时候，提到了 Xamarin.Forms。如果使用 Xamarin 的原生控件开发方式，那么开发者需要了解各个平台的界面布局规则。在有了 Xamarin.Forms 的情况下，开发者可以无需了解各个平台的布局即可开发出适用于各个平台的应用。当然，这其实引入了一个 UI 间接层，在 UI 的实现中绕了一下，可能不适用于开发稍微复杂一点的应用。当然这些都是以前就有的内容了。&lt;/p&gt;

&lt;p&gt;令人兴奋的是，随着 Xamarin，也推出了 AppCenter 服务！你再也不需要针对每一个应用市场走不同的发布流程了，你可以在只在 AppCenter 中完成 App Store 以及 Google Play 或者其他市场的发布流程。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://appcenter.ms/&quot;&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-31-58.png&quot; alt=&quot;Visual Studio App Center&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;net-core-3-与桌面应用&quot;&gt;.NET Core 3 与桌面应用&lt;/h3&gt;

&lt;p&gt;今年五月的 Microsoft Build 大会上就已经发布了重磅消息 .NET Core 3 将为桌面应用带来支持。不过主会场上与 .NET Core 3 相关的演示依然令人兴奋。&lt;/p&gt;

&lt;p&gt;演讲者使用 .NET Framework 版本的应用进行磁盘文件扫描，得出所有文件目录的百分比占用的时候用时 7109 毫秒。而相同的代码使用 .NET Core 3 编译，进行同样的操作，耗时是 1763 毫秒！&lt;/p&gt;

&lt;p&gt;不止如此，.NET Core 3 还提供了一种编译方式，将所有的第三方依赖通通发布成一个 exe，这样，你能直接带走一个什么依赖都不需要的单个 exe 文件进行分发；相比于此前 .NET Core 3 发布时一大堆第三方依赖包，或者 .NET Framework 时需要的 .NET Framework 环境依赖来说，简直是方便！&lt;/p&gt;

&lt;h2 id=&quot;思考方式&quot;&gt;思考方式&lt;/h2&gt;

&lt;p&gt;大会带来的不止是产品和商务的碰撞，不止是技术和推广和交流，更有思考方式上的改变。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;No problems can be solved from the same level of consciousness that created it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在“万物重构”分会场课程中，韦青老师将云比喻成电，将公有云比喻成电网，将私有云比喻成发电机。曾经在电网不那么稳定，是不是会停电的日子里，可能每家大院都会有一台发电机，为自家大院供电。不过共有电网因为雄厚的实力，其发展速度是非常快的，无论从稳定性、速度、可用性还有安全性上都好的太多，私有发电机渐渐会失去其作用。私有云就像发电机一样，是公有云发展过程中的中间方案。&lt;/p&gt;

&lt;p&gt;技术是一种赋能，其应用不止是在技术本身。例如电，早期对电的用途就在于发挥它的能源和光属性；很容易地就用电来驱动点灯。可能早期怎么都不能想到可以使用电来做现代计算机这样如此复杂和精密的任务。云，如此，不止用来存储和计算。这是一种赋能，是一种再创造。&lt;/p&gt;

&lt;h2 id=&quot;大佬&quot;&gt;大佬&lt;/h2&gt;

&lt;h3 id=&quot;edi-wang&quot;&gt;Edi Wang&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://edi.wang/ &quot;&gt;https://edi.wang/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-33-04.png&quot; alt=&quot;Edi Wang&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Edi Wang 是我们第一个遇到的大佬，一直活跃于 Windows 应用开发 UWP 方向。当然后面也在做 Asp.NET Core。&lt;/p&gt;

&lt;p&gt;Edi Wang 是个 100% 资深的软粉，所有的产品都是用微软的。歌单中还有“软狗之歌”……&lt;/p&gt;

&lt;h3 id=&quot;文轩&quot;&gt;文轩&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ithome.com/ &quot;&gt;https://www.ithome.com/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-34-45.png&quot; alt=&quot;蓝衣是玄隐，皮卡丘是文轩&quot; /&gt;&lt;br /&gt;
▲ 图片来自 IT 之家 &lt;a href=&quot;https://www.ithome.com/0/390/988.htm&quot;&gt;微软粉丝之夜：IT之家读者捕捉铺路集团董事长玄隐 - 微软,粉丝之夜,技术暨生态大会&lt;/a&gt;，蓝衣是玄隐，皮卡丘是文轩&lt;/p&gt;

&lt;p&gt;文轩的实名是白天宝，是 IT 之家的编辑。擅长写深度文章而不是资讯，做微软产品评测。在青岛 IT 之家，快讯一天需要三十篇，而深度文章则不需要那么多。&lt;/p&gt;

&lt;p&gt;文轩对对质量的要求很高。不止是自己的文章，还有形象，还有周围的环境以及给大家的体验。出发之前理了一个奇妙的发型，于是买了一只耳朵会动的皮卡丘帽子。在 MVP 之夜之前，将酒吧背景音乐换成微软在各大会场曾经播放过的音乐；将酒吧中所有的显示器图案换成 Microsoft Logo；将酒吧灯光换成与 Microsoft Logo 搭配的主题蓝色，并搭配一些让人舒适的暖色。他仔细校准每一个灯光，显示器中的显示效果，以求达到最佳的微软酒吧氛围。&lt;/p&gt;

&lt;p&gt;IT 之家的读者更多的是粉丝群体，学生很多。如果文章内容过于偏技术，那么不会有多少读者。所以可能一些非技术性或半技术性文章可以在 IT 之家发布和推广呢！&lt;/p&gt;

&lt;h3 id=&quot;梁桐铭&quot;&gt;梁桐铭&lt;/h3&gt;

&lt;p&gt;微信公众号：角落的白板报&lt;/p&gt;

&lt;p&gt;梁桐铭是一位 90 后，频繁活跃于 .NET 社区和成都 .NET 线下。他和张善友一起不断尽自己的努力改善国内的 .NET 生态环境。&lt;/p&gt;

&lt;p&gt;在课程《打造开源的 .NET Core 微服务解决方案》课程中，他介绍了 &lt;a href=&quot;https://www.52abp.com/&quot;&gt;52ABP.com&lt;/a&gt; 微服务框架，可以用在你的 Web 应用上。&lt;/p&gt;

&lt;h3 id=&quot;红薯&quot;&gt;红薯&lt;/h3&gt;

&lt;p&gt;红薯是&lt;a href=&quot;https://www.oschina.net/&quot;&gt;开源中国&lt;/a&gt;创始人，&lt;a href=&quot;https://gitee.com/&quot;&gt;码云&lt;/a&gt;创始人。拥抱开源，专治 GitHub 在中国的各种水土不服。&lt;/p&gt;

&lt;p&gt;码云为个人开发者提供免费的 git 仓库服务，为企业开发者提供付费的私有仓库服务。企业可以通过签署合同的方式来保证企业私有仓库的安全。&lt;/p&gt;

&lt;p&gt;期望码云能够与开发工具有更多的集成，或者可以提供 API，让社区来帮助完成这样的集成。&lt;/p&gt;

&lt;h3 id=&quot;吴波&quot;&gt;吴波&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.vichamp.com/ &quot;&gt;http://blog.vichamp.com/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;吴波是 PowerShell 方向的 MVP，在福建。对 .NET Framework 和 .NET Core 的迁移也是很感兴趣的。他推荐我们可以尝试使用 &lt;a href=&quot;https://turbo.net/studio&quot;&gt;Turbo Studio&lt;/a&gt; 对 .NET Framework 环境进行虚拟化，这样应用在部署之后可以不需要系统中安装的 .NET Framework 环境。&lt;/p&gt;

&lt;p&gt;他告诉我们，一般企业会非常支持这种对外的技术交流的，因为对公司来说：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;可以提高团队对外的技术影响力；&lt;/li&gt;
  &lt;li&gt;可以为团队塑造一个有应诉待查的 Leader，这样在团队中进行一些决策时大家更能够信服，容易推动一些技术方案的执行；&lt;/li&gt;
  &lt;li&gt;可以吸引技术人才，了解前沿技术。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;项斌&quot;&gt;项斌&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/BeanHsiang/ &quot;&gt;http://www.cnblogs.com/BeanHsiang/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 MVP 展台一直循环播放的视频中，有一段是校宝在线的。正好我们发现项斌就职于校宝在线。&lt;/p&gt;

&lt;p&gt;更多阅读：&lt;a href=&quot;https://mp.weixin.qq.com/s/qwd6HW4vLNCyGuynYgrFdw&amp;gt;&quot;&gt;鬼才项斌，用人工智能推动教育服务创新&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;邵猛&quot;&gt;邵猛&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/shaomeng/ &quot;&gt;https://www.cnblogs.com/shaomeng/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-36-37.png&quot; alt=&quot;邵猛&quot; /&gt;&lt;/p&gt;

&lt;p&gt;深圳来画科技的 UWP 版本开发者，在技术和商务上都是领导者。&lt;/p&gt;

&lt;h3 id=&quot;水歌&quot;&gt;水歌&lt;/h3&gt;

&lt;p&gt;水歌的名字是石垚（yáo），做 FCC 培训内容。FCC 全称 Free Code Campus。&lt;/p&gt;

&lt;p&gt;FCC 为学员免费提供各种语言的学习资料，可以在线从零开始学习编程语言。他为此提供一些课程内容。&lt;/p&gt;

&lt;p&gt;领英在没有与 FCC 有过任何沟通的情况下，把 FCC 认证作为大学认证同级别对待。&lt;/p&gt;

&lt;h3 id=&quot;张善友&quot;&gt;张善友&lt;/h3&gt;

&lt;p&gt;微信公众号：dotNET跨平台&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-37-21.png&quot; alt=&quot;张善友（右）和梁桐铭（左）&quot; /&gt;&lt;br /&gt;
▲ 张善友（右）和梁桐铭（左）&lt;/p&gt;

&lt;p&gt;张善友的名气已经不需要介绍了……&lt;/p&gt;

&lt;h3 id=&quot;dino&quot;&gt;Dino&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/dino623/default.html &quot;&gt;http://www.cnblogs.com/dino623/default.html &lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;windows-应用圈&quot;&gt;Windows 应用圈&lt;/h2&gt;

&lt;p&gt;微软主推的 Windows 桌面应用的开发框架是 UWP / WPF / Windows Forms，随后是原生 Win32 API。&lt;/p&gt;

&lt;p&gt;Windows 桌面应用的开发者在国内是真的少，传说中的后 PC 时代。27 号上午我的课程&lt;a href=&quot;/post/dotnet-build-and-roslyn-course-in-tech-summit-2018&quot;&gt;预编译框架，开发高性能应用&lt;/a&gt;还有很多人参加。虽然说主要适用于桌面应用，不过在思想上也能用于其他 .NET 领域（当然开源社区的小伙伴如果能多多支持也能都支持的）。而&lt;a href=&quot;https://www.cnblogs.com/shaomeng/&quot;&gt;邵猛&lt;/a&gt;下午的课程&lt;a href=&quot;http://www.cnblogs.com/shaomeng/p/9769270.html&quot;&gt;利用 Windows 新特性开发出更好的手绘视频应用&lt;/a&gt;听众就寥寥无几了。虽说那个时间已经有不少参会者走了，不过这与国内目前的 UWP（或者说推广到 Windows 桌面应用）生态不无关系。&lt;/p&gt;

&lt;h3 id=&quot;windows-应用-ui-框架的未来发展方向&quot;&gt;Windows 应用 UI 框架的未来发展方向&lt;/h3&gt;

&lt;p&gt;虽然微软会持续发力，但 UWP 在未来很长一段时间之内可能只会成为小众，除非找到下一个爆发点。这真的得益于微软在 Windows 应用分发平台上的各种失误（比如各种商店无法下载……）。&lt;/p&gt;

&lt;p&gt;在微软推广 UWP 的过程中，WPF 一直作为 UWP 的上一个遗落的版本而存在着，只会处于维护状态。微软会为其添加安全更新、添加 .NET Core 迁移相关的支持，而不会再在性能或者一些低概率的 Bug 上耗费太多精力。WPF 会在此状态下持续很长一段时间，因为微软必须考虑到自家内部产品中也存在大量基于 .NET Framework 的 WPF 和 Windows Forms 应用，比如 Visual Studio。在未来一两年内，WPF 和 Windows Forms 最大的变化要属其 .NET Core 化了，估计 Visual Studio 这种重磅的应用可能面临着两种不同的进化路线：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;向 .NET Core 方向迁移。
    &lt;ul&gt;
      &lt;li&gt;如果这种事情发生，那么至少 WPF 会在未来一个较短的数年之内获得不那么少的维护支持。至少必须确保 Visual Studio 这种大级别的 WPF 应用能够顺利完成迁移而不会产生大量明显的 Bug。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;向跨平台方向迁移。
    &lt;ul&gt;
      &lt;li&gt;这几乎相当于对 Visual Studio 的界面代码进行一次彻头彻尾的重构。不过考虑到插件的兼容性，也许未来一段时间之内这种跨平台迁移会采用混合界面框架。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Visual Studio 几乎不会往 UWP 的方向发展。&lt;/p&gt;

&lt;h3 id=&quot;未来的-windows-ui-框架选择&quot;&gt;未来的 Windows UI 框架选择&lt;/h3&gt;

&lt;p&gt;从对微软内部工具的迁移策略可以对我们的技术栈变化带来一些可能发展方向的思考。首先，如果是 WPF / Windows Forms，那么向 .NET Core 的迁移几乎成了一个 100% 要做的事情。无论未来 WPF / Windows Forms 产品要向哪个方向发展，只要不是整个团队的开发人员换掉，整个技术栈换掉，.NET Core 迁移都是必然的一步。这一步不止能带来大量的性能提升，还能让应用不再与操作系统产生大量的耦合，减少 .NET Framework 部署到操作系统中产生的一堆环境问题。&lt;/p&gt;

&lt;p&gt;那么可以采用的 UI 框架是什么呢？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Xamarin 方向&lt;/li&gt;
  &lt;li&gt;UWP 方向&lt;/li&gt;
  &lt;li&gt;混合 UI 框架方向&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于希望团队充分进行技术复用，那么几乎可以考虑迁移到以上 UI 框架，否则可以考虑其他 UI 框架。当然我更推荐使用混合 UI 框架，这样利于未来 UI 框架的演进，并充分利用到新 UI 框架带来的 Windows 新特性支持和高性能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-41-20.png&quot; alt=&quot;再见 Tech Summit 2018&quot; /&gt;&lt;br /&gt;
▲ 再见 Tech Summit 2018&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-31-14-39-35.png&quot; alt=&quot;高铁上的夜色&quot; /&gt;&lt;/p&gt;

&lt;p&gt;回家的时候，高铁上。再次进入了夜色……&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Nov 2018 05:08:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/tech-summit-2018.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/tech-summit-2018.html</guid>
        
        
        <category>miscellaneous</category>
        
      </item>
    
      <item>
        <title>只有你能 new 出来！.NET 隐藏构造函数的 n 种方法（Builder Pattern / 构造器模式）</title>
        <description>&lt;p&gt;如果你给类写了一个公有构造函数，那么这个类就能被其他开发者 new 出来。如果你不想让他们 new 出来，把构造函数 &lt;code class=&quot;highlighter-rouge&quot;&gt;private&lt;/code&gt; 就好了呀。&lt;/p&gt;

&lt;p&gt;然而还有更多奇怪的方式来隐藏你类的构造方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;tic&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;为什么要隐藏构造函数&quot;&gt;为什么要隐藏构造函数？&lt;/h2&gt;

&lt;p&gt;有些类型，只有组件的设计者才知道如何正确创建其类型的实例，多数开发者都无法正确将其创建出来。典型的如 &lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt;：绝大多数开发者都不能正确创建出 &lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt; 的实例，但通过写一个字符串由编译器去创建，或者使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;StringBuilder&lt;/code&gt; 来构造则不容易出错。&lt;/p&gt;

&lt;p&gt;再或者，我们只希望开发者使用到某个抽象的实例，而不是具体的类型，那么这个时候开发者也需要有方法能够拿到抽象接口的实例。我们可能会使用工厂或者某些其他的方法让开发者在不知道具体类型的时候获取到抽象类型的实例。&lt;/p&gt;

&lt;p&gt;这正是构造器模式的典型应用场景。在维基百科中对它适用性的描述为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;在以下情况使用生成器模式：&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时；&lt;/li&gt;
    &lt;li&gt;当构造过程必须允许被构造的对象有不同的表示时。&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;详见：&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%94%9F%E6%88%90%E5%99%A8%E6%A8%A1%E5%BC%8F&quot;&gt;生成器模式 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接下来，我们使用一些奇怪的方式来创建对象的实例，完完全全把构造函数隐藏起来。&lt;/p&gt;

&lt;h2 id=&quot;隐式转换和显式转换&quot;&gt;隐式转换和显式转换&lt;/h2&gt;

&lt;p&gt;典型的像 &lt;code class=&quot;highlighter-rouge&quot;&gt;long a = 1;&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;bool? b = true&lt;/code&gt; 这都是语法级别的隐式转换。这真的只是语法级别的隐式转换，实际上这两个都是编译器原生支持，编译时即已转换为真实的类型了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Versioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NonVersionable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Versioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NonVersionable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;explicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nullable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们可以考虑写一个神奇的类，其创建是通过隐式转换来实现的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Fantastic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fantastic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的输出是 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv is fantastic&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Patterns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Fantastic&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is fantastic.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而使用显式转换，我们还可以写出更奇怪的代码来。比如下面这个，我们的实例是通过强制转换一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 来实现的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Fantastic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fantastic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的输出是 ` is fantastic` 字符串。呃……前面有个空格。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Patterns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Fantastic&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFantastic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFantastic&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFantastic&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fantastic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is fantastic.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IFantastic&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;IFantastic&lt;/code&gt; 必须得是一个类，而不能是接口，因为隐式转换不能从接口转，也不能转到接口。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-20-20-30-36.png&quot; alt=&quot;不能定义从接口进行的隐式转换&quot; /&gt;&lt;br /&gt;
▲ 不能定义从接口进行的隐式转换&lt;/p&gt;

&lt;h2 id=&quot;运算符重载&quot;&gt;运算符重载&lt;/h2&gt;

&lt;p&gt;使用运算符重载，也可以让类型实例的构造隐藏起来。比如下面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Scope&lt;/code&gt; 类型，从字符串创建，然后通过与不同的字符串进行位或运算来得到其他的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Scope&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然这段代码也少不了隐式转换的作用。&lt;/p&gt;

&lt;p&gt;以上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Scope&lt;/code&gt; 类型的实现在 github 上开源，其表示 OAuth 2.0 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Scope&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/walterlv/ERMail/blob/master/src/ERMail.Core/OAuth/Scope.cs&quot;&gt;ERMail/Scope.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于运算符重载的更多内容，可以参考我的另外两篇文章：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/overridable-operators-in-csharp&quot;&gt;C# 中那些可以被重载的操作符，以及使用它们的那些丧心病狂的语法糖 - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/overload-null-coalescing-operator-in-csharp&quot;&gt;C# 空合并操作符（??）不可重载？其实有黑科技可以间接重载！ - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Nov 2018 05:08:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/hide-your-constructor.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/hide-your-constructor.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>都是用 DllImport？有没有考虑过自己写一个 extern 方法？</title>
        <description>&lt;p&gt;你做 .NET 开发的时候，一定用过 &lt;code class=&quot;highlighter-rouge&quot;&gt;DllImport&lt;/code&gt; 这个特性吧，这货是用于 P/Invoke (Platform Invoke, 平台调用) 的。这种 &lt;code class=&quot;highlighter-rouge&quot;&gt;DllImport&lt;/code&gt; 标记的方法都带有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 关键字。&lt;/p&gt;

&lt;p&gt;那么有没有可能我们自己写一个自己的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 方法呢？答案是可以的。本文就写一个这样的例子。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;dllimport&quot;&gt;DllImport&lt;/h2&gt;

&lt;p&gt;日常我们的平台调用代码是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;那个窗口的标题栏文字&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 此部分代码省略。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpWindowName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你看不到 &lt;code class=&quot;highlighter-rouge&quot;&gt;FindWindow&lt;/code&gt; 的实现。&lt;/p&gt;

&lt;h2 id=&quot;自定义的-extern&quot;&gt;自定义的 extern&lt;/h2&gt;

&lt;p&gt;那我们能否自己实现一个这样的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 的方法呢？写一写，还真是能写得出来的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-06-21-13-11.png&quot; alt=&quot;外部方法需要 Attribute 的提示&quot; /&gt;&lt;br /&gt;
▲ 外部方法需要 Attribute 的提示&lt;/p&gt;

&lt;p&gt;只不过如果你装了 ReSharper，会给出一个提示，告诉你外部方法应该写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 在上面（虽然实际上编译没什么问题）。&lt;/p&gt;

&lt;p&gt;那么我们就真的写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 在上面吧。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvHiddenMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AttributeUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeTargets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AllowMultiple&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inherited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvHiddenMethodAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你好奇如果没写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 会怎样，那我可以告诉你 —— 你写不写都一样，都是不能运行起来的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-06-21-20-12.png&quot; alt=&quot;方法没有实现&quot; /&gt;&lt;br /&gt;
▲ 方法没有实现&lt;/p&gt;

&lt;h2 id=&quot;让自定义的-extern-工作起来&quot;&gt;让自定义的 extern 工作起来&lt;/h2&gt;

&lt;p&gt;如果无法运行，那么我们写 &lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 是完全没有意义的。于是我们怎么能让这个“外部的”函数工作起来呢？—— 事实上就是工作不起来。&lt;/p&gt;

&lt;p&gt;不过，我们能够控制编译过程，能够在编译期间为其添加一个实现。&lt;/p&gt;

&lt;p&gt;这里，我们需要用到 MSBuild/Roslyn 相关的知识：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%80%9A%E8%BF%87-Target-%E4%BF%AE%E6%94%B9%E7%BC%96%E8%AF%91%E7%9A%84%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn 通过 Target 修改编译的文件 - 林德熙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当你读完上面那篇文章，你就明白我想干啥了。没错，在编译期间将其替换成一个拥有实现的函数。&lt;/p&gt;

&lt;p&gt;现在，我们将我们的几个类放到不同的文件中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-06-21-42-46.png&quot; alt=&quot;我们的项目文件&quot; /&gt;&lt;br /&gt;
▲ 我们的项目文件&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Program.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Demo.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WalterlvHiddenMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// WalterlvHiddenMethodAttribute.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AttributeUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeTargets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AllowMultiple&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inherited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WalterlvHiddenMethodAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Attribute&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No！我们还有一个隐藏文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo.implemented.cs&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-06-21-47-05.png&quot; alt=&quot;隐藏的文件&quot; /&gt;&lt;br /&gt;
▲ 隐藏的文件&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Demo.implemented.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;我就是一个外部方法。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个文件我是通过在 csproj 中将其 remove 掉使得在解决方案中看不见。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Demo.implemented.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，我们按照上文博客中所说的方式，添加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;，在编译时替换这个文件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net472&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Demo.implemented.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvReplaceMethod&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeforeBuild&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Remove=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Demo.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Demo.implemented.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，运行即会发现可以运行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-06-21-53-26.png&quot; alt=&quot;可以运行&quot; /&gt;&lt;br /&gt;
▲ 可以运行&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 是 C# 的一个语法而已，谁都可以用，但最终编译时的 C# 文件必须都有实现。&lt;/li&gt;
  &lt;li&gt;我们可以在编译时修改编译的文件来为这些未实现的方法添加实现。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;原理&quot;&gt;原理&lt;/h2&gt;

&lt;p&gt;看完上面的方法，是不是觉得写一个把实现藏起来的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extern&lt;/code&gt; 方法很简单？&lt;/p&gt;

&lt;p&gt;但如果你认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DllImport&lt;/code&gt; 也是这么做的那就不对了。&lt;/p&gt;

&lt;p&gt;还记得我们一开始写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FindWindow&lt;/code&gt; 方法吗？我们查看其编译后的 IL 代码，可以发现其外部调用已经写到了 IL 里面了，并且其实现使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;pinvokeimpl&lt;/code&gt; 关键字。也就是说，具体的调用是 JIT 编译器去做的事儿。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pinvokeimpl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unicode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;winapi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;native&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;FindWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpWindowName&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preservesig&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Can't find a body&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method Walterlv::FindWindow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于实际执行时的执行细节，可以阅读 &lt;a href=&quot;https://stackoverflow.com/a/14471704/6233938&quot;&gt;c# - How does DllImport really work? - Stack Overflow&lt;/a&gt; 了解更多。&lt;/p&gt;

&lt;p&gt;如果去看看我们写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的 IL，就完全不一样了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidebysig&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cil&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managed&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WalterlvHiddenMethodAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; 
      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxstack&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;IL_0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          
    &lt;span class=&quot;n&quot;&gt;IL_0001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ldstr&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;我就是一个外部方法。&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_0006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mscorlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IL_000b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nop&lt;/span&gt;          
    &lt;span class=&quot;n&quot;&gt;IL_000c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;          

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// end of method Demo::Foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这其实就是我们在 Demo.implement.cs 中写的那个函数的实现。这是当然，毕竟我们编译时偷偷把这个函数换成了那个隐藏的文件实现了。&lt;/p&gt;

&lt;p&gt;关于如何迅速查看 C# 代码对应的 IL，可以阅读我的另一篇博客：&lt;a href=&quot;/post/how-to-quickly-write-emit-code.html#%E5%BF%AB%E9%80%9F%E7%BC%96%E5%86%99-emit&quot;&gt;如何快速编写和调试 Emit 生成 IL 的代码&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/14471704/6233938&quot;&gt;c# - How does DllImport really work? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Nov 2018 05:08:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/write-your-own-extern-method.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/write-your-own-extern-method.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>roslyn</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>深入了解 WPF Dispatcher 的工作原理（PushFrame 部分）</title>
        <description>&lt;p&gt;在上一篇文章 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（Invoke/InvokeAsync 部分）&lt;/a&gt; 中我们发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Invoke&lt;/code&gt; 方法内部是靠 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 来确保“不阻塞地等待”的。然而它是怎么做到“不阻塞地等待”的呢？&lt;/p&gt;

&lt;p&gt;阅读本文将更深入地了解 Dispatcher 的工作机制。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是&lt;strong&gt;深入了解 WPF Dispatcher 的工作原理&lt;/strong&gt;系列文章的一部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;Invoke/InvokeAsync 部分&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;PushFrame 部分&lt;/a&gt;（本文）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;dispatcherpushframe-是什么&quot;&gt;Dispatcher.PushFrame 是什么？&lt;/h2&gt;

&lt;p&gt;如果说上一篇文章 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（Invoke/InvokeAsync 部分）&lt;/a&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 算是偏冷门的写法，那 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 总该写过吧？有没有好奇过为什么写 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 的地方可以等新开的窗口返回之后继续执行呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看来我们这次有必要再扒开 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 的源码看一看了。不过在看之前，我们先看一看 Windows Forms 里面 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoEvents&lt;/code&gt; 的实现，这将有助于增加我们对源码的理解。&lt;/p&gt;

&lt;h2 id=&quot;doevents&quot;&gt;DoEvents&lt;/h2&gt;

&lt;p&gt;Windows Forms 里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoEvents&lt;/code&gt; 允许你在执行耗时 UI 操作的过程中插入一段 UI 的渲染过程，使得你的界面看起来并没有停止响应。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SecurityPermissionAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Demand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityPermissionFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnmanagedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DispatcherFrame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherOperationCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExitFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExitFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;首先我们需要拿出本文一开始的结论——调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 可以在不阻塞 UI 线程的情况下等待。&lt;/p&gt;

&lt;p&gt;在此基础之上，我们仔细分析此源码的原理，发现是这样的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;添加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Background&lt;/code&gt;（4） 优先级的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherOperation&lt;/code&gt;，执行的操作就是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExitFrame&lt;/code&gt; 方法。（如果不明白这句话，请回过头再看看 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;Invoke/InvokeAsync 这部分&lt;/a&gt; 。）&lt;/li&gt;
  &lt;li&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 以便在不阻塞 UI 线程的情况下等待。&lt;/li&gt;
  &lt;li&gt;由于用户输入的优先级是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Input&lt;/code&gt;（5），UI 响应的优先级是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Loaded&lt;/code&gt;（6），渲染的优先级是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Render&lt;/code&gt;（7），每一个都比 &lt;code class=&quot;highlighter-rouge&quot;&gt;Background&lt;/code&gt;（4）高，于是只要有任何 UI 上的任务，都会先执行，直到没有任务时才会执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExiteFrame&lt;/code&gt; 方法。（如果不知道为什么，依然请回过头再看看 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;Invoke/InvokeAsync 这部分&lt;/a&gt; 。）&lt;/li&gt;
  &lt;li&gt;当 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExitFrame&lt;/code&gt; 被执行时，它会设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherFrame.Continue&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了让 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoEvents&lt;/code&gt; 实现它的目标，它必须能够在中间插入了 UI 和渲染逻辑之后继续执行后续代码才行。于是，我们可以大胆猜想，设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherFrame.Continue&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 的目标是让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame(frame);&lt;/code&gt; 这一句的等待结束，这样才能继续后面代码的执行。&lt;/p&gt;

&lt;p&gt;好了，现在我们知道了一个不阻塞等待的开关：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame(frame);&lt;/code&gt; 来不阻塞地等待；&lt;/li&gt;
  &lt;li&gt;设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;frame.Continue = false&lt;/code&gt; 来结束等待，继续执行代码。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;知道了这些，再扒 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.PushFrame&lt;/code&gt; 代码会显得容易许多。&lt;/p&gt;

&lt;h2 id=&quot;pushframe-的源码&quot;&gt;PushFrame 的源码&lt;/h2&gt;

&lt;p&gt;这真是一项神奇的技术。以至于这一次我需要毫无删减地贴出全部源码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurityCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityTreatAsSafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PushFrameImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherFrame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldSyncContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newSyncContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MSG&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MSG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
 
    &lt;span class=&quot;n&quot;&gt;_frameDepth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Change the CLR SynchronizationContext to be compatable with our Dispatcher.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;oldSyncContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newSyncContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DispatcherSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newSyncContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
                &lt;span class=&quot;nf&quot;&gt;TranslateAndDispatchMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
            &lt;span class=&quot;c1&quot;&gt;// If this was the last frame to exit after a quit, we&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// can now dispose the dispatcher.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_frameDepth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_hasShutdownStarted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;ShutdownImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Restore the old SynchronizationContext.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSynchronizationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldSyncContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_frameDepth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;--;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_frameDepth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We have exited all frames.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_exitAllFrames&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里有两个点值得我们研究：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;_frameDepth&lt;/code&gt; 字段。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环部分。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们先看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;_frameDepth&lt;/code&gt; 字段。每调用一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 就需要传入一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherFrame&lt;/code&gt;，在一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 期间再调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 则会导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;_frameDepth&lt;/code&gt; 字段增 1。于是，一个个的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherFrame&lt;/code&gt; 就这样一层层嵌套起来。&lt;/p&gt;

&lt;p&gt;再看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;TranslateAndDispatchMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还记得 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoEvents&lt;/code&gt; 节里我们说到的开关吗？就是这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;frame.Continue&lt;/code&gt;。看到这段代码是不是很明确了？如果设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，则退出循环，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 方法返回，同时 &lt;code class=&quot;highlighter-rouge&quot;&gt;_frameDepth&lt;/code&gt; 字段减 1。在一个个的 &lt;code class=&quot;highlighter-rouge&quot;&gt;frame.Continue&lt;/code&gt; 都设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 以至于后，程序将从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数退出。&lt;/p&gt;

&lt;p&gt;如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;frame.Continue&lt;/code&gt; 一直保持为 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 呢？那就进入了“死循环”。可是这里我们需要保持清醒，因为“死循环”意味着阻塞，意味着无法在中间插入其它的 UI 代码。所以要么是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 让我们能继续处理窗口消息，要么是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TranslateAndDispatchMessage&lt;/code&gt; 让我们能继续处理窗口消息。（至于为什么只要能处理消息就够了，我们上一篇说到过，&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 任务队列的处理就是利用了 Windows 的消息机制。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-26-03-33-28.png&quot; alt=&quot;消息循环&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，这两个方法内部都调用到了非托管代码，很难通过阅读代码了解到它处理消息的原理。但是通过 .NET Framework 源码调试技术我发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;TranslateAndDispatchMessage&lt;/code&gt; 方法似乎并没有被调用到，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 始终在执行。我们有理由相信用于实现非阻塞等待的关键在 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 方法内部。.NET Framework 源码调试技术请参阅：&lt;a href=&quot;http://blog.lindexi.com/lindexi//post/%E8%B0%83%E8%AF%95-ms-%E6%BA%90%E4%BB%A3%E7%A0%81/&quot;&gt;调试 ms 源代码 - 林德熙&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;于是去 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetMessage&lt;/code&gt; 方法内，找到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsafeNativeMethods.ITfMessagePump&lt;/code&gt; 类型的变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;messagePump&lt;/code&gt;。这是 Windows 消息循环中的重要概念。看到这里，似乎需要更了解消息循环才能明白实现非阻塞等待的关键。不过我们可以再次通过调试 .NET Framework 的源码来了解消息循环在其中做的重要事情。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;调试源码以研究-pushframe-不阻塞等待的原理&quot;&gt;调试源码以研究 PushFrame 不阻塞等待的原理&lt;/h2&gt;

&lt;p&gt;为了开始调试，我为主窗口添加了触摸按下的事件处理函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnStylusDown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusDownEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Invoke&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 都是为了执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 而写的代码。&lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine()&lt;/code&gt; 只是为了让我打上一个用于观察的断点。&lt;/p&gt;

&lt;p&gt;运行程序，在每一次触摸主窗口的时候，我们都会命中一次断点。观察 Visual Studio 的调用堆栈子窗口，我们会发现每触摸一次命中断点时调用堆栈中会多一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;，继续执行，由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 又会多一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;。于是，我们每触摸一次，调用堆栈中会多出两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;每次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 之后，都会经历一次托管到本机和本机到托管的转换，随后是消息处理。我们的触摸消息就是从消息处理中调用而来。&lt;/p&gt;

&lt;p&gt;于是可以肯定，每一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 都将开启一个新的消息循环，由非托管代码开启。当 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowDialog&lt;/code&gt; 出来的窗口关掉，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 执行完毕，或者其它会导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 退出循环的代码执行时，就会退出一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 带来的消息循环。于是，在上一次消息处理中被 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 阻塞的代码得以继续执行。一层层退出，直到最后 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数退出时，程序结束。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-26-03-47-20.png&quot; alt=&quot;PushFrame 的嵌套&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图使用的是我在 GitHub 上的一款专门研究 WPF 触摸原理的测试项目：&lt;a href=&quot;https://github.com/walterlv/ManipulationDemo&quot;&gt;https://github.com/walterlv/ManipulationDemo&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;至此，&lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 能够做到不阻塞 UI 线程的情况下继续响应消息的原理得以清晰地梳理出来。&lt;/p&gt;

&lt;p&gt;如果希望更详细地了解 WPF 中的 Dispatcher 对消息循环的处理，可以参考：&lt;a href=&quot;http://blog.csdn.net/royyeah/article/details/4785473&quot;&gt;详解WPF线程模型和Dispatcher - 踏雪无痕 - CSDN博客&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;每一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 都会开启一个新的消息循环，记录 &lt;code class=&quot;highlighter-rouge&quot;&gt;_frameDepth&lt;/code&gt; 加 1；&lt;/li&gt;
  &lt;li&gt;在新的消息循环中，会处理各种各样的 Windows 消息，其中有的以事件的形式转发，有的是执行加入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PriorityQueue&amp;lt;DispatcherOperation&amp;gt;&lt;/code&gt; 队列中的任务；&lt;/li&gt;
  &lt;li&gt;在显式地退出 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 时，新开启的消息循环将退出，并继续此前 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 处的代码执行；&lt;/li&gt;
  &lt;li&gt;当所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 都退出后，程序结束。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt; 循环是真的阻塞着主线程，但循环内部会处理消息循环，以至于能够不断地处理新的消息，看起来就像没有阻塞一样。（这与我们平时随便写代码阻塞主线程导致无法处理消息还是有区别的。）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;pushframe-的已知缺陷&quot;&gt;PushFrame 的已知缺陷&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 使用通过开启一个新的消息循环使得 UI 线程能够在新的消息循环中处理消息，以便 UI “不卡”，同时使得调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 的代码能够 “阻塞”。&lt;/p&gt;

&lt;p&gt;这种实现方式也带来了一些问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;调用代码被虽然被阻塞，但又不像常规线程阻塞一样 —— 它会发生 “意料之外” 的重入问题，即单个线程也会遇到并发问题。
    &lt;ul&gt;
      &lt;li&gt;关于重入，可以阅读：&lt;a href=&quot;/post/reentrancy-in-async-method&quot;&gt;异步任务中的重新进入（Reentrancy）&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 使用 Windows 消息循环机制，而多重消息循环机制可能出现其他 Bug，例如：
    &lt;ul&gt;
      &lt;li&gt;当你在用鼠标拖拽窗口调整位置或大小的时候，如果触发了一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt;，那么此窗口会卡住
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/19411613/6233938&quot;&gt;c# - PushFrame locks up WPF window when user is moving window - Stack Overflow&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;PushFrame/DispatcherFrame
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs&quot;&gt;Dispatcher.cs&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/33002966/wpf-dispatcherframe-magic-how-and-why-this-works&quot;&gt;c# - WPF DispatcherFrame magic - how and why this works? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/41759665/for-what-is-pushframe-needed&quot;&gt;c# - For what is PushFrame needed? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/2665191/wpf-dispatcher-pushframe&quot;&gt;multithreading - WPF - Dispatcher PushFrame() - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherframe.aspx&quot;&gt;DispatcherFrame Class (System.Windows.Threading)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/152137/DispatcherFrame-Look-in-Depth&quot;&gt;DispatcherFrame. Look in-Depth - CodeProject&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 消息循环
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Message_loop_in_Microsoft_Windows&quot;&gt;Message loop in Microsoft Windows - Wikipedia&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/11417216/understanding-the-dispatcher-queue/11419762&quot;&gt;c# - Understanding the Dispatcher Queue - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/royyeah/article/details/4785473&quot;&gt;详解WPF线程模型和Dispatcher - 踏雪无痕 - CSDN博客&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;调试 .NET Framework 源码
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://blog.lindexi.com/lindexi//post/%E8%B0%83%E8%AF%95-ms-%E6%BA%90%E4%BB%A3%E7%A0%81/&quot;&gt;调试 ms 源代码 - 林德熙&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;已知缺陷
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/19411613/6233938&quot;&gt;c# - PushFrame locks up WPF window when user is moving window - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Nov 2018 05:08:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet/2017/09/26/dispatcher-push-frame.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet/2017/09/26/dispatcher-push-frame.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>用 dotTrace 进行性能分析时，Timeline 打不开？无法启动进程？也许你需要先开启系统性能计数器的访问权限</title>
        <description>&lt;p&gt;对 .NET 程序使用 dotTrace 进行性能分析时，你也可能遭遇到 dotTrace 的 Bug。我就遇到了性能分析选项 Timeline 打不开进程的情况。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;dottrace-的性能分析选项&quot;&gt;dotTrace 的性能分析选项&lt;/h2&gt;

&lt;p&gt;dotTrace 启动性能分析的选项有四个，你可以阅读 &lt;a href=&quot;/post/dottrace-profiler-options&quot;&gt;用 dotTrace 进行性能分析时，各种不同性能分析选项的含义和用途&lt;/a&gt; 了解不同选项的含义和用途，以便对你的性能分析提供更多的帮助和更有价值的分析数据。&lt;/p&gt;

&lt;h2 id=&quot;timeline-打不开&quot;&gt;Timeline 打不开？&lt;/h2&gt;

&lt;p&gt;可是，当我真的使用这个选项的时候，却发现根本无法完成性能分析。&lt;/p&gt;

&lt;p&gt;具体来说，是在出现了性能分析的指示窗口后，被分析程序的界面迟迟没有出现。随后在半分钟到数分钟后，分析器自动退出，没有得到任何性能分析数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-08-58.png&quot; alt=&quot;启动 ETW&quot; /&gt;&lt;br /&gt;
▲ 启动 ETW（事件跟踪器）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-09-03.png&quot; alt=&quot;启动分析器&quot; /&gt;&lt;br /&gt;
▲ 启动性能分析器&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-40-48.png&quot; alt=&quot;性能分析指示窗口&quot; /&gt;&lt;br /&gt;
▲ 性能分析指示窗口&lt;/p&gt;

&lt;p&gt;最后那个指示窗口就这样过一会儿变成以下窗口，提示 “Waiting for a managed application to start…”，然后消失。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-41-35.png&quot; alt=&quot;等待启动&quot; /&gt;&lt;br /&gt;
▲ 等待启动&lt;/p&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;

&lt;p&gt;在这四个选项中，只有 Timeline 和 Line-by-line 是打不开的，Sampling 打得开。于是可以从他们之间的差异着手分析。&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/dottrace-profiler-options&quot;&gt;用 dotTrace 进行性能分析时，各种不同性能分析选项的含义和用途&lt;/a&gt; 一文中，我们可以得知，只有 Timeline 用到了 ETW，而这个是一个系统功能。也许是系统功能无法访问呢？毕竟这种事情还是非常常见的。&lt;/p&gt;

&lt;p&gt;于是果然在 &lt;a href=&quot;https://dotnettools-support.jetbrains.com/hc/en-us/articles/206546069-Timeline-is-not-working-issue-solution-for-different-Windows-versions&quot;&gt;Timeline is not working&lt;/a&gt; 找到了解决方法 —— 你需要开启你所在的用户组对 Performance Monitor 的访问权限。&lt;/p&gt;

&lt;p&gt;现在开始解决：&lt;/p&gt;

&lt;h3 id=&quot;启动计算机管理&quot;&gt;启动“计算机管理”&lt;/h3&gt;

&lt;p&gt;在你的 Windows 10 搜索（或者小娜）中搜索 “计算机管理”，英文用户搜索 “Computer Management”。然后启动它。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-26-14.png&quot; alt=&quot;搜索并启动计算机管理&quot; /&gt;&lt;br /&gt;
▲ 搜索并启动计算机管理&lt;/p&gt;

&lt;h3 id=&quot;配置性能监视器用户组&quot;&gt;配置性能监视器用户组&lt;/h3&gt;

&lt;p&gt;在计算机管理中，找到 “计算机管理 -&amp;gt; 系统工具 -&amp;gt; 本地用户和组 -&amp;gt; 组”，点开后在中间的列表中找到 “Performance Monitor Users”。&lt;/p&gt;

&lt;p&gt;对于英文的系统，对应的路径为 “Computer Management -&amp;gt; System Tools -&amp;gt; Local Users and Groups -&amp;gt; Groups”，然后一样找到 “Performance Monitor Users”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-27-56.png&quot; alt=&quot;找到 Performance Monitor Users&quot; /&gt;&lt;br /&gt;
▲ 找到 Performance Monitor Users&lt;/p&gt;

&lt;p&gt;为了照顾中文用户，我找小伙伴帮忙截了一张中文的图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-31-36.png&quot; alt=&quot;中文版的设置路径&quot; /&gt;&lt;br /&gt;
▲ 中文版的设置路径&lt;/p&gt;

&lt;h3 id=&quot;添加自己作为用户组的成员&quot;&gt;添加自己作为用户组的成员&lt;/h3&gt;

&lt;p&gt;双击 Performance Monitor Users，按照以下的步骤将自己添加到用户组中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-35-51.png&quot; alt=&quot;将自己添加到用户组中&quot; /&gt;&lt;br /&gt;
▲ 将自己添加到用户组中&lt;/p&gt;

&lt;p&gt;感谢 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 再次帮我截到一张中文版的图片：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-11-12-16-36-39.png&quot; alt=&quot;中文版的添加&quot; /&gt;&lt;br /&gt;
▲ 中文版的添加&lt;/p&gt;

&lt;h3 id=&quot;你已经完成了&quot;&gt;你已经完成了&lt;/h3&gt;

&lt;p&gt;你已经修复了问题，建议注销并重新登录 Windows，当然也可以考虑重启。&lt;/p&gt;

&lt;p&gt;重启后再次尝试使用 Timeline 选项启动进程进行性能分析应该可以正常。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnettools-support.jetbrains.com/hc/en-us/articles/206546069-Timeline-is-not-working-issue-solution-for-different-Windows-versions&quot;&gt;Timeline is not working: issue solution for different Windows versions – .NET Tools Support - JetBrains&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dotnettools-support.jetbrains.com/hc/en-us/community/posts/207099769-Can-t-start-ETW-collector-&quot;&gt;Can’t start ETW collector. – .NET Tools Support - JetBrains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 12 Nov 2018 08:46:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dottrace-timeline-not-working.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dottrace-timeline-not-working.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>了解 .NET/C# 程序集的加载时机，以便优化程序启动性能</title>
        <description>&lt;p&gt;林德熙在 &lt;a href=&quot;https://blog.lindexi.com/post/C-%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%95%B0%E9%87%8F%E5%AF%B9%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;C# 程序集数量对软件启动性能的影响&lt;/a&gt; 一文中说到程序集数量对程序启动性能的影响。在那篇文章中，我们得出结论，想同类数量的情况下，程序集的数量越多，程序启动越慢。&lt;/p&gt;

&lt;p&gt;额外的，不同的代码编写方式对程序集的加载性能也有影响。本文将介绍 .NET 中程序集的加载时机，了解这个时机能够对启动期间程序集的加载性能带来帮助。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;程序集加载方式对性能的影响&quot;&gt;程序集加载方式对性能的影响&lt;/h2&gt;

&lt;p&gt;为了直观地说明程序集加载方式对性能的影响，我们先来看一段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StartupLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startupManagerTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StartupManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigAssemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Xxx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Yyy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Zzz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Www&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startupManagerTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Xxx&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Yyy&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Zzz&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Www&lt;/code&gt; 分别在不同的程序集中，我们姑且认为程序集名称是 FooAssembly、BarAssembly、XxxAssembly、YyyAssembly、ZzzAssembly、WwwAssembly。&lt;/p&gt;

&lt;p&gt;现在，我们统计 Main 函数开始第一句话到 Run 函数开始执行时的时间：&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;统计&lt;/td&gt;
      &lt;td&gt;Milestone&lt;/td&gt;
      &lt;td&gt;Time&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;——————————–&lt;/td&gt;
      &lt;td&gt;——-:&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;107&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;344&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第二次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;106&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第二次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;276&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第三次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;89&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第三次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;224&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在三次统计中，我们可以看到三次平均时长 180 ms。如果观察没一句执行时的 Module，可以看到 Main 函数开始时，这些程序集都未加载，而 Run 函数执行时，这些程序集都已加载。&lt;/p&gt;

&lt;p&gt;事实上，如果你把断点放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Run&lt;/code&gt; 中 lambda 表达式的第一个括号处，你会发现那一句时这些程序集就已经加载了，不用等到后面代码的执行。&lt;/p&gt;

&lt;p&gt;作为对比，我需要放上没有程序集加载时候的数据（具体来说，就是去掉所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 那些类的代码）：&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;统计&lt;/td&gt;
      &lt;td&gt;Milestone&lt;/td&gt;
      &lt;td&gt;Time&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;——————————–&lt;/td&gt;
      &lt;td&gt;——-:&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;43&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;75&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第二次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第二次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;35&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第三次&lt;/td&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td&gt;28&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第三次&lt;/td&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td&gt;40&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这可以证明，以上时间大部分来源于程序集的加载，而不是其他什么代码。&lt;/p&gt;

&lt;p&gt;现在，我们稍稍修改一下程序集，让 &lt;code class=&quot;highlighter-rouge&quot;&gt;new Foo()&lt;/code&gt; 改为使用 lambda 表达式来创建：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    using System;
    using System.Threading.Tasks;
    
    namespace Walterlv.Demo
    {
        public static class Program
        {
            [STAThread]
            private static int Main(string[] args)
            {
                var logger = new StartupLogger();
                var startupManagerTask = Task.Run(() =&amp;gt;
                {
                    var startup = new StartupManager(logger).ConfigAssemblies(
&lt;span class=&quot;gd&quot;&gt;--                      new Foo(),
--                      new Bar(),
--                      new Xxx(),
--                      new Yyy(),
--                      new Zzz(),
--                      new Www());
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;++                      () =&amp;gt; new Foo(),
++                      () =&amp;gt; new Bar(),
++                      () =&amp;gt; new Xxx(),
++                      () =&amp;gt; new Yyy(),
++                      () =&amp;gt; new Zzz(),
++                      () =&amp;gt; new Www());
&lt;/span&gt;                    startup.Run();
                    return startup;
                });
    
                var app = new App(startupManagerTask);
                app.InitializeComponent();
                app.Run();
    
                return 0;
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，直到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 函数执行时，那些程序集都还没有加载。由于我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 函数中真正使用到了那些对象，所以其实 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 中是需要写代码来加载那些程序集的（也是自动）。&lt;/p&gt;

&lt;p&gt;如果我们依次加载这些程序集，那么时间如下：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Milestone&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Time&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;739&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;如果我们使用 Parallel 并行加载这些程序集，那么时间如下：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Milestone&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Time&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Main Method Start&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;31&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Run&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;493&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;可以看到，程序集加载时间有明显增加。&lt;/p&gt;

&lt;p&gt;实际上我们完成的任务是一样的，但是程序集加载时间显著增加，这显然不是我们期望的结果。&lt;/p&gt;

&lt;p&gt;在上例中，第一个不到 200 ms 的加载时间，来源于我们直接写下了 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 不同程序集中的类型。后面长一些的时间，则因为我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数中没有直接构造类型，而是写成了 lambda 表达式。来源于在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 中调用那些 lambda 表达式从而间接加载了类型。&lt;/p&gt;

&lt;p&gt;为了更直观，我把 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 方法中的关键代码贴出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// assemblies 是直接 new 出来的参数传进来的。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_assembliesToBeManaged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// assemblies 是写的 lambda 表达式参数传进来的。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_assembliesToBeManaged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的版本，这些程序集的加载时间是 180 ms，而下面的版本，则达到惊人的 701 ms！&lt;/p&gt;

&lt;h2 id=&quot;程序集的加载时机&quot;&gt;程序集的加载时机&lt;/h2&gt;

&lt;p&gt;于是我们可以了解到程序集的加载时机。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在一个方法被 JIT 加载的时候，里面用到的类型所在的程序集就会被加载到应用程序域中。当加载完后，此方法才被执行。&lt;/li&gt;
  &lt;li&gt;加载程序集时，只会加载方法中会直接使用到的类型，如果是 lambda 内的类型，则会在此 lambda 被调用的时候才会执行（其实这本质上和方法被调用之前的加载是一个时机）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;并且，我们能够得出性能优化建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果可行，最好让 CLR 自动管理程序集的加载，而且一次性能加载所有程序集的话就一次性加载，而不要尝试自己去分开加载这些程序集，那会使得能够并行的加载程序集的时间变得串行，浪费启动性能。&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 11 Nov 2018 11:06:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/when-assemblies-are-loaded.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/when-assemblies-are-loaded.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>出让执行权：Task.Yield, Dispatcher.Yield</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Yield&lt;/code&gt; 这个词很有意思，叫做“屈服”“放弃”“让步”，字面意义上是让出当前任务的执行权，转而让其他任务可以插入执行。&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Thread&lt;/code&gt; 都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Yield()&lt;/code&gt; 方法，看起来都可以让出当前任务的执行权。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;p&gt;如果在阅读中发现对本文涉及到的一些概念不太明白，可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（Invoke/InvokeAsync 部分）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-push-frame.html&quot;&gt;深入了解 WPF Dispatcher 的工作原理（PushFrame 部分）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;dispatcheryield&quot;&gt;Dispatcher.Yield&lt;/h2&gt;

&lt;p&gt;如果一个方法的实现比较耗时，为了不影响 UI 的响应，你会选择用什么方法呢？我之前介绍过的 &lt;a href=&quot;/post/dotnet/2017/09/26/dispatcher-invoke-async.html&quot;&gt;Invoke 和 InvokeAsync&lt;/a&gt; 可以解决，将后续耗时的任务分割成一个个小的片段以低于用户输入和渲染的优先级执行。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield&lt;/code&gt; 也可以，其行为更加类似于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.InvokeAsync&lt;/code&gt;（即采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 调度的方式，事实上后面会说到其实就是调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt;），而非 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Invoke&lt;/code&gt;（即采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushFrame&lt;/code&gt; 新开消息循环的方式）。&lt;/p&gt;

&lt;p&gt;使用时需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoWorkWhichWillTakeHalfASecond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 将在每遍历到一个集合项的时候中断一次，让 UI 能够响应用户的交互输入和渲染。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Yield&lt;/code&gt; 方法可以传入一个优先级参数，指示继续执行后续任务的优先级。默认是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Background&lt;/code&gt;，低于用户输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Input&lt;/code&gt;、 UI 逻辑 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Loaded&lt;/code&gt; 和渲染 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriority.Render&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield&lt;/code&gt; 是如何做到出让执行权的呢？&lt;/p&gt;

&lt;p&gt;查看源码，发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherYield&lt;/code&gt; 的返回值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherPriorityAwaiter&lt;/code&gt;，而它的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 方法是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SRID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatcherPriorityAwaiterInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以，其实真的就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt;。如果希望了解为何是 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnCompleted&lt;/code&gt; 方法，可以阅读 &lt;a href=&quot;http://liujiajia.me/blog/details/csharp-multi-threading-05-csharp6-08-customize-awaitable&quot;&gt;【C#】【多线程】【05-使用C#6.0】08-自定义awaitable类型 - L.M&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;需要注意&quot;&gt;需要注意&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 类型的静态方法，而不是像 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeAsync&lt;/code&gt; 一样是实例方法。不过 C# 有一个神奇的特性——静态方法和实例方法可以在同一上下文中调用，而不用担心产生歧义。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DispatcherObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 调用静态方法 Yield。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 调用实例方法 InvokeAsync。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意需要引用命名空间 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Threading&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;taskyield&quot;&gt;Task.Yield&lt;/h2&gt;

&lt;p&gt;拿前面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield&lt;/code&gt; 的例子，我们换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoWorkWhichWillTakeHalfASecond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;效果与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield(DispatcherPriority.Normal)&lt;/code&gt; 是一样的。因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 调度回到线程上下文靠的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt;，WPF UI 线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 被设置为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt;，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 调度；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt; 构造时传入的优先级默认是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Normal&lt;/code&gt;，WPF 并没有特殊传入一个别的值，所以 WPF UI 线程上使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 出让执行权后，恢复时使用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Normal&lt;/code&gt; 优先级，相当于 Dispatcher.Yield(DispatcherPriority.Normal)。&lt;/p&gt;

&lt;p&gt;希望了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 的区别可以阅读 &lt;a href=&quot;https://stackoverflow.com/a/24672061/6233938&quot;&gt;c# - Difference between Synchronization Context and Dispatcher - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherSynchronizationContext&lt;/code&gt; 执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 后续任务的上下文代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     Asynchronously invoke the callback in the SynchronizationContext.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendOrPostCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Call BeginInvoke with the cached priority.  Note that BeginInvoke&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// preserves the behavior of passing exceptions to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Dispatcher.UnhandledException unlike InvokeAsync.  This is&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// desireable because there is no way to await the call to Post, so&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// exceptions are hard to observe.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;既然是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Normal&lt;/code&gt; 优先级，那么在 UI 线程上的效果自然不如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.Yield&lt;/code&gt;。但是，&lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield&lt;/code&gt; 适用于任何线程，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;SynchronizationContext&lt;/code&gt; 本身是与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt; 无关的，适用于任何线程。这样，于如果一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 内部的任务太耗时，用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield&lt;/code&gt; 则可以做到将此任务分成很多个片段执行。&lt;/p&gt;

&lt;p&gt;如果觉得 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Yield()&lt;/code&gt; 的用途难以理解，可以参考 &lt;a href=&quot;https://www.cnblogs.com/dudu/&quot;&gt;dudu&lt;/a&gt; 的博客 &lt;a href=&quot;https://www.cnblogs.com/dudu/p/task-yield.html&quot;&gt;终于明白了 C# 中 Task.Yield 的用途 - dudu - 博客园&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;chrome-extension://klbibkeccnjlkjkiokjodocebajanakg/suspended.html#ttl=c%23%20-%20Task.Yield%20-%20real%20usages%3F%20-%20Stack%20Overflow&amp;amp;uri=https://stackoverflow.com/questions/23431595/task-yield-real-usages&quot;&gt;c# - Task.Yield - real usages? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.yield%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;Task.Yield Method (System.Threading.Tasks)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/24671883/difference-between-synchronization-context-and-dispatcher&quot;&gt;c# - Difference between Synchronization Context and Dispatcher - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 29 Oct 2018 05:03:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/yield-in-task-dispatcher.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/yield-in-task-dispatcher.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>课程 预编译框架，开发高性能应用 - 微软技术暨生态大会 2018</title>
        <description>&lt;p&gt;微软技术暨生态大会（Tech Summit），2018 年在上海世博中心召开。这是最后一次的 Tech Summit 了；明年开始，中国大陆地区就要和其他国家和地区一样，进行全球 Ignite Tour 了。&lt;/p&gt;

&lt;p&gt;我也有幸成为分会场讲师团队的一员，课程是《预编译框架 - 开发高性能应用》。内容就是我博客中与 MSBuild / Roslyn / dotnet / NuGet 相关的内容；我们将利用这些知识打造一个高性能客户端应用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.microsoft.com/china/techsummit/2018/&quot;&gt;&lt;img src=&quot;/static/posts/2018-10-14-20-08-21.png&quot; alt=&quot;微软技术暨生态大会&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;进入 &lt;a href=&quot;https://www.microsoft.com/china/techsummit/2018/&quot;&gt;微软技术暨生态大会&lt;/a&gt; 官网可以了解更多内容。如果你和我一样对微软技术富有热情，那么也欢迎你 &lt;a href=&quot;http://www.mstechsummit.cn/Ticket/BuyTicket&quot;&gt;买票&lt;/a&gt; 一起去上海。&lt;/p&gt;

&lt;h2 id=&quot;关于课程预编译框架---开发高性能应用&quot;&gt;关于课程《预编译框架 - 开发高性能应用》&lt;/h2&gt;

&lt;p&gt;时间：2018 年 10 月 27 日 11:00-11:45
代号：DEV306
难度：L300&lt;/p&gt;

&lt;p&gt;利用 Roslyn 在编译期间提前完成收集和修改所需的各种信息，我们能将 .NET 的反射耗时降低到近乎为 0！
当前大多数的框架都离不开反射的支持，但是 .NET 的反射很伤性能，而不用反射又很难支撑大型应用；基于 Roslyn 的预编译框架旨在解决这些性能问题。&lt;/p&gt;

&lt;p&gt;本次讲题能学到什么？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;体验预编译框架的强大性能&lt;/li&gt;
  &lt;li&gt;理解 dotnet build 的编译过程&lt;/li&gt;
  &lt;li&gt;使用 Roslyn 分析和修改项目源代码&lt;/li&gt;
  &lt;li&gt;如何开发自己的预编译框架&lt;/li&gt;
  &lt;li&gt;制作源代码引用 NuGet 包（而不是 dll 引用 NuGet 包）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其实此课程的计划课程内容有 2.5 小时，毕竟博客都有好几十篇了呢。算上跟我一起研究这项技术的 &lt;a href=&quot;https://blog.lindexi.com/post/roslyn.html&quot;&gt;林德熙的与 Roslyn 相关的博客&lt;/a&gt;，那就更多了，而且还在持续增加中。不过实际分会场课程中内容众多，留给每个讲师的时间只有 45 分钟，必须减少和压缩课程内容。&lt;/p&gt;

&lt;p&gt;于是，实际课程会以入门为主，进阶内容将作为资料线下学习。注意：即便是“入门”，难度也依然是 L300（难度范围为 L100-L400），所以你必须拥有一定的 .NET 开发知识和一些应用开发经验才会理解课程内容。如果你的经验更偏客户端应用开发，那么更能体会本课程内容的目的。&lt;/p&gt;

&lt;p&gt;额外的，彩排在 26 号 9:40-10:00。&lt;/p&gt;

&lt;h2 id=&quot;课程大纲&quot;&gt;课程大纲&lt;/h2&gt;

&lt;p&gt;课程大纲是为 2 小时的课程而设计的。所以实际上我只会讲大部分内容，以下所有动手实验和演示的地方都会略过，代之以提前运行和编写的结果。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;实际课程中会略过的部分以斜体表示&lt;/em&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;引入
    &lt;ul&gt;
      &lt;li&gt;一批性能数据
        &lt;ul&gt;
          &lt;li&gt;收集：反射 VS 配置文件 VS 预编译&lt;/li&gt;
          &lt;li&gt;调用：直接调用 VS 最快反射 VS 预编译&lt;/li&gt;
          &lt;li&gt;程序集个数：1 个 VS ……&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;概览
    &lt;ul&gt;
      &lt;li&gt;目录
        &lt;ul&gt;
          &lt;li&gt;了解源码包 SourceYard 和预编译框架 SourceFusion&lt;/li&gt;
          &lt;li&gt;学会编写编译期代码以提升应用性能&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;预编译框架的原理&lt;/li&gt;
      &lt;li&gt;多个 API 的展示
        &lt;ul&gt;
          &lt;li&gt;源码包&lt;/li&gt;
          &lt;li&gt;&lt;em&gt;编译期类&lt;/em&gt;&lt;/li&gt;
          &lt;li&gt;类模板&lt;/li&gt;
          &lt;li&gt;&lt;em&gt;扩展&lt;/em&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;教学
    &lt;ul&gt;
      &lt;li&gt;目标：学会使用编译期代码代替反射以提升应用的执行性能&lt;/li&gt;
      &lt;li&gt;教学目录
        &lt;ul&gt;
          &lt;li&gt;理解 dotnet build 的编译过程
            &lt;ul&gt;
              &lt;li&gt;理解 csproj 文件格式&lt;/li&gt;
              &lt;li&gt;理解编译过程&lt;/li&gt;
              &lt;li&gt;理解 NuGet 打包原理&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;em&gt;动手实验：SourceYard 源码包简化版&lt;/em&gt;
            &lt;ul&gt;
              &lt;li&gt;&lt;em&gt;编译期间执行一个 exe 程序&lt;/em&gt;&lt;/li&gt;
              &lt;li&gt;&lt;em&gt;在 exe 程序中接收参数并干预编译结果&lt;/em&gt;&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;学习使用 Roslyn 分析源代码
            &lt;ul&gt;
              &lt;li&gt;语法可视化窗格&lt;/li&gt;
              &lt;li&gt;分析 C# 语法树&lt;/li&gt;
              &lt;li&gt;在编译期间执行代码&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;em&gt;动手实验：SourceFusion 预编译框架简化版&lt;/em&gt;
            &lt;ul&gt;
              &lt;li&gt;&lt;em&gt;在编译期间收集程序集中具有特定标记的所有类型&lt;/em&gt;&lt;/li&gt;
              &lt;li&gt;&lt;em&gt;生成代码以快速访问这些类型的特定方法&lt;/em&gt;&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;回顾
    &lt;ul&gt;
      &lt;li&gt;回顾 dotnet build 的编译过程和 Roslyn 分析源码&lt;/li&gt;
      &lt;li&gt;SourceYard 和 SourceFusion 的开源仓库，欢迎加入&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;关于讲师--吕毅&quot;&gt;关于讲师 —— 吕毅&lt;/h2&gt;

&lt;p&gt;你可以进入 &lt;a href=&quot;https://www.mstechsummit.cn/SpeakerSession/Index&quot;&gt;微软技术暨生态大会 - 大会日程&lt;/a&gt; 页面，然后点击 “演讲嘉宾”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-20-25-57.png&quot; alt=&quot;演讲嘉宾&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这里，你可以看到主题演讲、分会场课程以及动手实验室的各位讲师，可以去了解每一位讲师以及他们的课程。当然，你也可以看到我。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-20-24-49.png&quot; alt=&quot;讲师页面&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;欢迎你的加入&quot;&gt;欢迎你的加入&lt;/h2&gt;

&lt;p&gt;最后，欢迎你一起参加微软技术暨生态大会，我们一起去与微软大咖，各位微软 MVP，社区技术牛人交流技术。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mstechsummit.cn/Ticket/BuyTicket&quot;&gt;购票&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://forms.office.com/Pages/ResponsePage.aspx?id=-mDpt2weQ0S0nX_yMOmiFX9DC0n9uYxEjysPXp0Mf7tURjBRVllaWklWU1NTU1NaVUs0TTdOR1VFTC4u&amp;amp;from=groupmessage&amp;amp;isappinstalled=0&quot;&gt;微软粉丝之夜报名（目前名额已满）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;课程课件使用 &lt;a href=&quot;https://easinote.seewo.com/&quot;&gt;希沃白板 5&lt;/a&gt; 制作，这是一款专门针对教学场景设计的互动课件工具。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-20-33-17.png&quot; alt=&quot;希沃白板 5&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;相关链接&quot;&gt;相关链接&lt;/h2&gt;

&lt;p&gt;由于相关文章太多，所以重新整理到以下新的文章中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/posts-for-learning-dotnet-build-nuget-roslyn&quot;&gt;从零开始学习 dotnet 编译过程和 Roslyn 源码分析 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多课程&quot;&gt;更多课程&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/shaomeng/p/9769270.html&quot;&gt;Microsoft Tech Summit 2018 课程简述：利用 Windows 新特性开发出更好的手绘视频应用 - shaomeng - 博客园&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;时间：2018 年 10 月 27 日 17:00-17:45&lt;/li&gt;
      &lt;li&gt;讲师：邵猛&lt;/li&gt;
      &lt;li&gt;代号：NUE204&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 19 Oct 2018 05:24:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/dotnet-build-and-roslyn-course-in-tech-summit-2018.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/dotnet-build-and-roslyn-course-in-tech-summit-2018.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>msbuild</category>
        
        <category>roslyn</category>
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
      </item>
    
      <item>
        <title>WPF 中的 NameScope</title>
        <description>&lt;p&gt;我们在 WPF 中使用绑定时可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ElementName=Foo&lt;/code&gt; 这样的写法，并且还能够真的在运行时找到这个名称对应的对象，是因为 WPF 中提供了名称范围概念。&lt;/p&gt;

&lt;p&gt;实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;INameScope&lt;/code&gt; 接口可以定义一个名称范围。无论你使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt; 属性还是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Name&lt;/code&gt; 特性都可以在一个名称范围内指定某个元素的名称。绑定时就在此名称范围内查找，于是可以找到你需要的对象。&lt;/p&gt;

&lt;p&gt;本文将介绍 WPF 中 NameScope 的查找规则。（额外的，资源 / 资源字典的查找方式与 NameScope 的方式是一样的，所以本文分析过程同样使用与资源的查找。）&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;inamescope&quot;&gt;INameScope&lt;/h2&gt;

&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;INameScope&lt;/code&gt; 接口只用来管理一个范围之内的名称。它包含下面三个方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;INameScope&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scopedElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UnregisterName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它的主要实现是 &lt;code class=&quot;highlighter-rouge&quot;&gt;NameScope&lt;/code&gt;，包含了更多功能；而上面的接口是其本质功能。&lt;/p&gt;

&lt;p&gt;不过，&lt;code class=&quot;highlighter-rouge&quot;&gt;NameScope&lt;/code&gt; 的实现带来了一个重要的依赖项属性 —— &lt;code class=&quot;highlighter-rouge&quot;&gt;NameScope&lt;/code&gt;。下面是此属性的代码（经过简化）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NameScopeProperty&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NameScope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetNameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameScopeProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetNameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameScopeProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样实现了此接口的还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;TemplateNameScope&lt;/code&gt;，此 NameScope 会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkTemplate&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElementFactory&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;BamlRecordReader&lt;/code&gt; 设置到以上依赖属性中。于是我们可以在模板范围内找到某个特定名称对应的元素。&lt;/p&gt;

&lt;p&gt;除此之外，NameScope 的设置由 XAML 解析器在 WPF 项目编译的时候自动生成。&lt;/p&gt;

&lt;h2 id=&quot;namescope-的名称注册规则&quot;&gt;NameScope 的名称注册规则&lt;/h2&gt;

&lt;p&gt;如果你没有在代码中显式去调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;RegisterName&lt;/code&gt; 这样的方法，那么 NameScope 的创建以及名称的注册都由 XAML 解析器来完成。&lt;/p&gt;

&lt;p&gt;XAML 解析器（BamlRecordReader）注册名字的时候并没有去爬可视化树什么的，只是单纯在解析 XAML 的时候去调用代码注册这个名字而已。注册由一个 Stack 来完成，&lt;code class=&quot;highlighter-rouge&quot;&gt;NameScopeStack&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;设想以下这个例子（来自于 .NET Framework 代码中的注释）：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myWindow&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    ...
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myStyle&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        ...
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myBrush&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/SolidColorBrush&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每当 XAML 解析器解析一层的时候，就会给 &lt;code class=&quot;highlighter-rouge&quot;&gt;NameScopeStack&lt;/code&gt; 入栈，于是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 首先创建 NameScope 入栈。随后解析到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 时又加一个 NameScope 入栈，其他元素解析时不会创建 NameScope（包括 XAML 中的顶层元素 &lt;code class=&quot;highlighter-rouge&quot;&gt;UserControl&lt;/code&gt; 等）。&lt;/p&gt;

&lt;p&gt;这时，&lt;code class=&quot;highlighter-rouge&quot;&gt;myWindow&lt;/code&gt; 会被注册到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 一层的 NameScope 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;myStyle&lt;/code&gt; 也会注册到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 一层的 NameScope 中；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;myBrush&lt;/code&gt; 则会注册到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style&lt;/code&gt; 那一层的 NameScope 中。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Window 的 NameScope
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myWindow&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myStyle&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Style 的 NameScope
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myBrush&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;namescope-的名称查找规则&quot;&gt;NameScope 的名称查找规则&lt;/h2&gt;

&lt;p&gt;在本文一开始贴出 &lt;code class=&quot;highlighter-rouge&quot;&gt;NameScope&lt;/code&gt; 依赖项属性的时候，你应该注意到这只是一个普通的属性，并没有使用到什么可以用可视化树继承这样的高级元数据。事实上也不应该有这样的高级元数据，因为 NameScope 的抽象级别低于可视化树或者逻辑树。&lt;/p&gt;

&lt;p&gt;但是，实际上 &lt;code class=&quot;highlighter-rouge&quot;&gt;NameScope&lt;/code&gt; 的查找却是依赖于逻辑树的 —— 这是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt; 的功能：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scopeOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;INameScope&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NameScopeFromObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;scopeOwner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogicalTreeHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindMentor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InheritanceContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;scopeOwner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;非常明显，&lt;code class=&quot;highlighter-rouge&quot;&gt;FindScope&lt;/code&gt; 是期望使用逻辑树来查找名称范围的。&lt;/p&gt;

&lt;p&gt;不过值得注意的是，当一个元素没有逻辑父级的时候，会试图使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Helper.FindMentor&lt;/code&gt; 来查找另一个对象。那这是什么方法，又试图寻找什么对象呢？&lt;/p&gt;

&lt;p&gt;Mentor 是名词，意为 “导师，指导”。于是我们需要阅读以下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Helper.FindMentor&lt;/code&gt; 方法的实现来了解其意图：&lt;/p&gt;

&lt;p&gt;提示：&lt;em&gt;以下注释中的 FE 代表 FrameworkElement，而 FCE 代表 FrameworkContentElement。&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     This method finds the mentor by looking up the InheritanceContext&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     links starting from the given node until it finds an FE/FCE. This&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     mentor will be used to do a FindResource call while evaluating this&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     expression.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     This method is invoked by the ResourceReferenceExpression&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     and BindingExpression&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindMentor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Find the nearest FE/FCE InheritanceContext&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;FrameworkContentElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DowncastToFEorFCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InheritanceContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体来说，是不断查找 &lt;code class=&quot;highlighter-rouge&quot;&gt;InheritanceContext&lt;/code&gt;，如果找到了 FrameworkElement 或者 FrameworkContentElement，那么就返回这个 FE 或者 FCE；如果到最终也没有找到，则返回 null。&lt;/p&gt;

&lt;p&gt;这是个 &lt;code class=&quot;highlighter-rouge&quot;&gt;virtual&lt;/code&gt; 属性，基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject&lt;/code&gt; 中只返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，而子类重写它时，返回父级。&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkContentElement&lt;/code&gt; 等重写了这个属性。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;FrameworkElement&lt;/code&gt;，重写时只是单纯的返回了一个内部管理的字段而已：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InheritanceContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InheritanceContextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此字段在调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject.AddInheritanceContext&lt;/code&gt; 的时候会赋值。而对于可视化树或逻辑树的建立，此方法不会被调用，所以此属性并不会对可视化树或逻辑树有影响。但是，&lt;code class=&quot;highlighter-rouge&quot;&gt;Freezable&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;InputBinding&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual3D&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;GridViewColumn&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewBase&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;CollectionViewSource&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceDictionary&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;TriggerAction&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;TriggerBase&lt;/code&gt; 等会在属性赋值的时候调用此方法。于是我们能够在以上这些属性的设置中找到名称。&lt;/p&gt;

&lt;p&gt;特别说明，只有那些重写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;InheritanceContext&lt;/code&gt; 的类型才会在查找名称的时候找得到 NameScope；只有以上这些调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyObject.AddInheritanceContext&lt;/code&gt; 方法的属性才会在赋值是能够找得到 NameScope。&lt;/p&gt;

&lt;p&gt;所以，我另一篇文章中所说的 ContextMenu 是找不到对应的 NameScope 的。&lt;a href=&quot;/post/fix-wpf-binding-issues-in-context-menu&quot;&gt;WPF 的 ElementName 在 ContextMenu 中无法绑定成功？试试使用 x:Reference！&lt;/a&gt;。此文中 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt; 找到的 NameScope 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Tue, 16 Oct 2018 13:01:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/namescope-of-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/namescope-of-wpf.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>将 UWP 中 CommandBar 的展开方向改为向下展开</title>
        <description>&lt;p&gt;在 UWP 中使用 CommandBar 来迅速添加一组功能按钮是非常迅速的，是 UWP 中推荐的交互方案之一。也许你能见到 CommandBar 按你所需向下展开，不过可能更多数情况会看到 CommandBar 的展开方向是向上的。&lt;/p&gt;

&lt;p&gt;本文将解释 CommandBar 的展开方向逻辑，并且提供多种方法来解决它展开方向的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;为什么我们需要更改-commandbar-的展开方向&quot;&gt;为什么我们需要更改 CommandBar 的展开方向？&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;CommandBar&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#40000000&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ClosedDisplayMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Compact&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Add&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;添加&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolTipService.ToolTip=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;添加一个 RSS 订阅&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bullets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;编辑&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolTipService.ToolTip=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;进入编辑状态&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CommandBar&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看下图的例子，我们有一个在顶部的 CommandBar，但是它展开的时候方向是向上的，以至于挡住了顶部的标题栏。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-28-commandbar-open-in-unexpected-direction.gif&quot; alt=&quot;CommandBar 在不合适的方向展开&quot; /&gt;&lt;br /&gt;
▲ CommandBar 在不合适的方向展开&lt;/p&gt;

&lt;p&gt;理论上标题栏是挡不住的。不过，由于流畅设计（Fluent Design）的存在，越来越多的应用开始使用自定义的标题栏，以获得浑然天成的流畅设计效果。而上图就是其中的一个例子。&lt;/p&gt;

&lt;p&gt;我们当然希望在顶部的 CommandBar 其展开方向是向下，所以我们需要找到一些方法。&lt;/p&gt;

&lt;h2 id=&quot;将-commandbar-改为向下展开的几种方法&quot;&gt;将 CommandBar 改为向下展开的几种方法&lt;/h2&gt;

&lt;p&gt;首先定一个基调：CommandBar 的默认展开方向就是向上，无论你使用哪种方式，本质上都没有解决其展开方向的问题。&lt;/p&gt;

&lt;p&gt;所以以下方法都有可能在你的使用场景下失效，除了大杀器 —— 重写 Template。&lt;/p&gt;

&lt;h3 id=&quot;方法一使用-pagetopappbar-属性&quot;&gt;方法一：使用 Page.TopAppBar 属性&lt;/h3&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Rssman.MainPage&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{ThemeResource ApplicationPageBackgroundThemeBrush}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Page.TopAppBar&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;CommandBar&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#40000000&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ClosedDisplayMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Compact&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Add&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;添加&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolTipService.ToolTip=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;添加一个 RSS 订阅&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppBarButton&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Icon=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bullets&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;编辑&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ToolTipService.ToolTip=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;进入编辑状态&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/CommandBar&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page.TopAppBar&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你并没有做一些奇怪的样式，是一个 Demo 或者是刚开始做的应用，那么此方法应该对你有效。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-28-commandbar-in-top-app-bar.gif&quot; alt=&quot;Page.TopAppBar 中的 CommandBar&quot; /&gt;&lt;br /&gt;
▲ Page.TopAppBar 中的 CommandBar&lt;/p&gt;

&lt;p&gt;看！现在 CommandBar 向下展开了。这就是我们的解决方案之一。&lt;/p&gt;

&lt;p&gt;不过，觉得怪怪的是不是？因为我自定义了标题栏，当然不能让标题栏挡住我的控件啊！&lt;/p&gt;

&lt;p&gt;千万不要尝试将你的 Page 设置一个 Margin 让他下移，因为：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-28-commandbar-in-top-app-bar.gif&quot; alt=&quot;Page.TopAppBar 是应用窗口级别的&quot; /&gt;&lt;br /&gt;
▲ 无论你设置到哪个 Page 中，无论 Margin 设为多少，就算是给 Frame 外面的 Grid 设置 Margin，通通都是无效的！Page.TopAppBar 在应用窗口级别的。&lt;/p&gt;

&lt;p&gt;正如官网中所描述的那样：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Command bars can be placed at the top of the app window, at the bottom of the app window, and inline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;方法二更改布局使得顶部空间不足以展开-commandbar&quot;&gt;方法二：更改布局，使得顶部空间不足以展开 CommandBar&lt;/h3&gt;

&lt;p&gt;CommandBar 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClosedDisplayMode&lt;/code&gt; 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compact&lt;/code&gt; 时，折叠状态高度 48，展开状态高度 60；在设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Minimal&lt;/code&gt; 时，折叠状态高度 24，展开状态依然是 60。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-05-16-43-23.png&quot; alt=&quot;这种模式下的展开和折叠高度&quot; /&gt;&lt;br /&gt;
▲ 各种模式下的展开和折叠高度&lt;/p&gt;

&lt;p&gt;鉴于 CommandBar 仅在空间不足时才会从向上展开变为向下展开，所以我们可以利用顶部空间的距离差来完成方向的修改。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compact&lt;/code&gt; 模式，我们仅能在上方预留不足 12 的尺寸，而对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Minimal&lt;/code&gt; 模式，我们则有不大于 36 的尺寸可以预留。&lt;/p&gt;

&lt;p&gt;在我们一开始的例子中，我们需要留出标题栏的高度，而标题栏高度为 32，所以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Minimal&lt;/code&gt; 模式时，我们的展开方向自然因为顶部空间不足而向下展开。另外，12 像素除了留白以外也没什么作用，所以实质上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compact&lt;/code&gt; 模式并不能通过这种方式解决展开方向的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-05-minimal-expand.gif&quot; alt=&quot;在使用 Minimal 的关闭模式时，可以向下展开&quot; /&gt;&lt;br /&gt;
▲ 在使用 Minimal 的关闭模式时，可以向下展开&lt;/p&gt;

&lt;p&gt;如果你设置的 SecondaryCommand 比较长，那么展开的时候也会占用较多的控件，于是也可以强制 CommandBar 向下展开。&lt;/p&gt;

&lt;h3 id=&quot;方法三设置-defaultlabelposition-避开展开方向的问题&quot;&gt;方法三：设置 DefaultLabelPosition 避开展开方向的问题&lt;/h3&gt;

&lt;p&gt;如果不容易改展开方向，那么不让 CommandBar 面临展开方向的问题也是一个不错的解决方案 —— 为 CommandBar 设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;DefaultLabelPosition&lt;/code&gt; 便是这样的方案。&lt;/p&gt;

&lt;p&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;DefaultLabelPosition&lt;/code&gt; 属性设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Right&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Collapsed&lt;/code&gt; 而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bottom&lt;/code&gt;，那么 CommandBar 便不再需要展开这些按钮了，因为即便展开也不会显示更多的信息了，除了那个根本不会影响高度的更多项。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-05-17-06-56.png&quot; alt=&quot;设置为 Collapsed 或者 Right 的 DefaultLabelPosition&quot; /&gt;&lt;br /&gt;
▲ 设置为 Collapsed 或者 Right 的 DefaultLabelPosition&lt;/p&gt;

&lt;h3 id=&quot;方法四修改-commandbar-的模板&quot;&gt;方法四：修改 CommandBar 的模板&lt;/h3&gt;

&lt;p&gt;不得不说这真是一个令人难受的方法，因为定义 CommandBar 模板和样式的代码行数有 1400 行左右。但这也是目前依然使用 CommandBar 控件时最好的方案了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-05-17-20-17.png&quot; alt=&quot;编辑控件模板的副本&quot; /&gt;&lt;br /&gt;
▲ 编辑控件模板的副本&lt;/p&gt;

&lt;p&gt;现在，使用 Visual Studio 设计器来帮助我们获得 CommandBar 的完整默认样式定义，就像上图那样。于是，我们可以阅读其代码并修改展开方向了。&lt;/p&gt;

&lt;p&gt;代码很长，为了能够迅速理解其结构，我将其最关键的大纲部分贴到下面：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CommandBarTemplate1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisplayModeStates&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualStateGroup.Transitions&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenClosed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateGroup.Transitions&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CompactOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimalOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenClosed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenUp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HiddenOpenDown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateGroup&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到，对于每一种 &lt;code class=&quot;highlighter-rouge&quot;&gt;ClosedDisplayMode&lt;/code&gt;，都有三种状态与之对应 —— Closed、Up 和 Down。当然，Up 就是向上展开时的状态，Down 就是向下展开时的状态。而 Closed、Up 和 Down 之间的状态切换有四种 —— Closed 到 Up、Up 到 Closed、Closed 到 Down 以及 Down 到 Closed。&lt;/p&gt;

&lt;p&gt;于是，我们要获得任何时候都向下展开的能力，我们便需要将所有的 Up 状态修改成 Down 的状态。&lt;/p&gt;

&lt;p&gt;现在，我们将 将 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState From=&quot;CompactClosed&quot; To=&quot;CompactOpenDown&quot; /&amp;gt;&lt;/code&gt; 的代码复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState From=&quot;CompactClosed&quot; To=&quot;CompactOpenUp&quot; /&amp;gt;&lt;/code&gt; 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState From=&quot;CompactOpenDown&quot; To=&quot;CompactClosed&quot; /&amp;gt;&lt;/code&gt; 内部的代码复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState From=&quot;CompactOpenUp&quot; To=&quot;CompactClosed&quot; /&amp;gt;&lt;/code&gt; 中，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState x:Name=&quot;CompactOpenDown&quot; /&amp;gt;&lt;/code&gt; 内的代码复制到 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;VisualState x:Name=&quot;CompactOpenUp&quot; /&amp;gt;&lt;/code&gt; 中。&lt;/p&gt;

&lt;p&gt;也就是说，我们将所有 CompactClosed 和 CompactDown 的状态复制到了 CompactClosed 和 CompactUp 的状态中。这样，即便 CommandBar 判定为向上展开，实际上的动画和交互也都是向下展开的了。&lt;/p&gt;

&lt;p&gt;以下是这样修改后的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-05-expand-by-editing-template.gif&quot; alt=&quot;使用样式更改的展开方向&quot; /&gt;&lt;br /&gt;
▲ 使用样式更改的展开方向&lt;/p&gt;

&lt;h2 id=&quot;究竟应该如何修改-commandbar-的展开方向&quot;&gt;究竟应该如何修改 CommandBar 的展开方向&lt;/h2&gt;

&lt;p&gt;在多数情况下，我想我们并没有特别强烈的需求一定要让 CommandBar 在顶部依然有空间的情况下展开方向向下。&lt;/p&gt;

&lt;p&gt;如果有，那通常也是中大型项目，这时 CommandBar 样式和模板所占用的那 1400 行左右的代码也就不显得多了。&lt;/p&gt;

&lt;p&gt;但对于小型个人项目而言，可以考虑修改应用程序的外观设计来规避这么长的代码。例如让 CommandBar 始终显示或隐藏文字，或者让 CommandBar 默认为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Minimal&lt;/code&gt; 的状态。&lt;/p&gt;

&lt;p&gt;如果你对其他控件有小型样式的修改需求，可以阅读我的另一篇文章：&lt;a href=&quot;/post/uwp-lightweight-xaml-styling&quot;&gt;UWP 轻量级样式定义（Lightweight Styling）&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Tue, 16 Oct 2018 08:57:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/force-the-commandbar-to-open-down.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/force-the-commandbar-to-open-down.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>各个 C# 版本的主要特性、发布日期和发布方式（C# 1.0 - 7.3）</title>
        <description>&lt;p&gt;本文收集各个 C# 版本的主要特性、发布日期和发布方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;c-80&quot;&gt;C# 8.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;尚在预览版本&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-73&quot;&gt;C# 7.3&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2018 年 5 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2017 v15.7 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-72&quot;&gt;C# 7.2&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2017 年 11 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2017 v15.5 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-71&quot;&gt;C# 7.1&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2017 年 8 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2017 v15.3 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-70&quot;&gt;C# 7.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2017 年 3 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2017 和 .NET Framework 4.7 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-60&quot;&gt;C# 6.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2015 年 7 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2015 和 .NET Framework 4.6 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-50&quot;&gt;C# 5.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2012 年 8 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2012 和 .NET Framework 4.5 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-40&quot;&gt;C# 4.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2010 年 4 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2010 和 .NET Framework 4.0 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-30&quot;&gt;C# 3.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2007 年 11 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2008 和 .NET Framework 3.5 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-20&quot;&gt;C# 2.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2005 年 11 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2005 和 .NET Framework 3.0 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-12&quot;&gt;C# 1.2&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2003 年 4 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2003 和 .NET Framework 1.1 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-10&quot;&gt;C# 1.0&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;2002 年 1 月&lt;/li&gt;
  &lt;li&gt;随 Visual Studio 2002 和 .NET Framework 1.0 发布&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/247623/6233938&quot;&gt;.net - What are the correct version numbers for C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 15 Oct 2018 07:00:20 +0000</pubDate>
        <link>https://blog.walterlv.com/post/csharp-version-histories.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/csharp-version-histories.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 的 ElementName 在 ContextMenu 中无法绑定成功？试试使用 x:Reference！</title>
        <description>&lt;p&gt;在 Binding 中使用 ElementName 司空见惯，没见它出过什么事儿。不过当你预见 ContextMenu，或者类似 Grid.Row / Grid.Column 这样的属性中设置的时候，ElementName 就不那么管用了。&lt;/p&gt;

&lt;p&gt;本文将解决这个问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;以下代码是可以正常工作的&quot;&gt;以下代码是可以正常工作的&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.BindingContext.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvWindow&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv Binding Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LightGray&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1 1 1 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MinHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在代码中，我们为一段文字中的一个部分绑定了主窗口的的一个属性，于是我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ElementName&lt;/code&gt; 来指定绑定源为 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvWindow&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-11-11-23.png&quot; alt=&quot;使用普通的 ElementName 绑定&quot; /&gt;&lt;br /&gt;
▲ 使用普通的 ElementName 绑定&lt;/p&gt;

&lt;h2 id=&quot;以下代码就无法正常工作了&quot;&gt;以下代码就无法正常工作了&lt;/h2&gt;

&lt;p&gt;保持以上代码不变，我们现在新增一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt;，然后在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt; 中使用一模一样的绑定表达式：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.BindingContext.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvWindow&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv Binding Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LightGray&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1 1 1 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MinHeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ContextMenu&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContextMenu&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MenuItem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Header=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ContextMenu&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ContextMenu&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，&lt;code class=&quot;highlighter-rouge&quot;&gt;MenuItem&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Header&lt;/code&gt; 属性设置为和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Run&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Text&lt;/code&gt; 属性一模一样的绑定字符串。不过运行之后的截图显示，右键菜单中并没有如预期般出现绑定的字符串。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-11-10-17.png&quot; alt=&quot;在 ContextMenu 中使用了 ElementName 绑定&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用-xreference-代替-elementname-能够解决&quot;&gt;使用 x:Reference 代替 ElementName 能够解决&lt;/h2&gt;

&lt;p&gt;以上绑定失败的原因，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid.ContextMenu&lt;/code&gt; 属性中赋值的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt; 不在可视化树中，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt; 又不是一个默认建立 ScopeName 的控件，此时既没有自己指定 NameScope，有没有通过可视化树寻找上层设置的 NameScope，所以在绑定上下文中是找不到 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvWindow&lt;/code&gt; 的。如果调用去查找，得到的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。详见：&lt;a href=&quot;/post/namescope-of-wpf&quot;&gt;WPF 中的 NameScope&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;类似的情况也发生在设置非可视化树或逻辑树的属性时，典型的比如在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid.Row&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid.Column&lt;/code&gt; 属性上绑定时，&lt;code class=&quot;highlighter-rouge&quot;&gt;ElementName&lt;/code&gt; 也是失效的。&lt;/p&gt;

&lt;p&gt;此时最适合的情况是直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Reference&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Window x:Class=&quot;Walterlv.Demo.BindingContext.MainWindow&quot;
          xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
          xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
          x:Name=&quot;WalterlvWindow&quot; Title=&quot;Walterlv Binding Demo&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
      &amp;lt;Grid Background=&quot;LightGray&quot; Margin=&quot;1 1 1 0&quot; MinHeight=&quot;40&quot;&amp;gt;
          &amp;lt;Grid.ContextMenu&amp;gt;
              &amp;lt;ContextMenu&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-                 &amp;lt;MenuItem Header=&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                 &amp;lt;MenuItem Header=&quot;{Binding Source={x:Reference WalterlvWindow}, Path=DemoText, Mode=OneWay}&quot; /&amp;gt;
&lt;/span&gt;              &amp;lt;/ContextMenu&amp;gt;
          &amp;lt;/Grid.ContextMenu&amp;gt;
          &amp;lt;TextBlock&amp;gt;
              &amp;lt;Run Text=&quot;{Binding Mode=OneWay}&quot; FontSize=&quot;20&quot; /&amp;gt;
              &amp;lt;LineBreak /&amp;gt;
              &amp;lt;Run Text=&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot; /&amp;gt;
          &amp;lt;/TextBlock&amp;gt;
      &amp;lt;/Grid&amp;gt;
  &amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，这是个假象，因为此代码运行时会抛出异常：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;XamlObjectWriterException: Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. The affected MarkupExtensions are:&lt;br /&gt;
‘System.Windows.Data.Binding’ Line number ‘8’ and line position ‘27’.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因为给 &lt;code class=&quot;highlighter-rouge&quot;&gt;MenuItem&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Header&lt;/code&gt; 属性绑定赋值的时候，创建绑定表达式用到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvWindow&lt;/code&gt;，但此时 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvWindow&lt;/code&gt; 尚在构建（因为里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContextMenu&lt;/code&gt; 是窗口的一部分），于是出现了循环依赖。而这是不允许的。&lt;/p&gt;

&lt;p&gt;为了解决循环依赖问题，我们可以考虑将 &lt;code class=&quot;highlighter-rouge&quot;&gt;x:Reference&lt;/code&gt; 放到资源中。因为资源是按需创建的，所以这不会造成循环依赖。&lt;/p&gt;

&lt;p&gt;那么总得有一个对象来承载我们的绑定源。拿控件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Tag&lt;/code&gt; 属性也许是一个方案，不过专门为此建立一个绑定代理类也许是一个更符合语义的方法：&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;Window x:Class=&quot;Walterlv.Demo.BindingContext.MainWindow&quot;
          xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
          xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
&lt;span class=&quot;gi&quot;&gt;+         xmlns:local=&quot;clr-namespace:Walterlv.Demo.BindingContext&quot;
&lt;/span&gt;          x:Name=&quot;WalterlvWindow&quot; Title=&quot;Walterlv Binding Demo&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+     &amp;lt;Window.Resources&amp;gt;
+         &amp;lt;local:BindingProxy x:Key=&quot;WalterlvBindingProxy&quot; Data=&quot;{x:Reference WalterlvWindow}&quot; /&amp;gt;
+     &amp;lt;/Window.Resources&amp;gt;
&lt;/span&gt;      &amp;lt;Grid Background=&quot;LightGray&quot; Margin=&quot;1 1 1 0&quot; MinHeight=&quot;40&quot;&amp;gt;
          &amp;lt;Grid.ContextMenu&amp;gt;
              &amp;lt;ContextMenu&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-                 &amp;lt;MenuItem Header=&quot;{Binding Source={x:Reference WalterlvWindow}, Path=DemoText, Mode=OneWay}&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                 &amp;lt;MenuItem Header=&quot;{Binding Source={StaticResource WalterlvBindingProxy}, Path=Data.DemoText, Mode=OneWay}&quot; /&amp;gt;
&lt;/span&gt;              &amp;lt;/ContextMenu&amp;gt;
          &amp;lt;/Grid.ContextMenu&amp;gt;
          &amp;lt;TextBlock&amp;gt;
              &amp;lt;Run Text=&quot;{Binding Mode=OneWay}&quot; FontSize=&quot;20&quot; /&amp;gt;
              &amp;lt;LineBreak /&amp;gt;
              &amp;lt;Run Text=&quot;{Binding ElementName=WalterlvWindow, Path=DemoText, Mode=OneWay}&quot; /&amp;gt;
          &amp;lt;/TextBlock&amp;gt;
      &amp;lt;/Grid&amp;gt;
  &amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于 &lt;code class=&quot;highlighter-rouge&quot;&gt;BindingProxy&lt;/code&gt;，非常简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BindingProxy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Freezable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BindingProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Freezable&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateInstanceCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BindingProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;BindingProxy: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Binding Proxy: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在运行，右键菜单已经正常完成了绑定。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-10-14-11-09-13.png&quot; alt=&quot;右键菜单完成了绑定&quot; /&gt;&lt;br /&gt;
▲ 右键菜单已经正常完成了绑定&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/32879146/6233938&quot;&gt;c# - WPF databinding error in Tag property - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 14 Oct 2018 04:25:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-wpf-binding-issues-in-context-menu.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-wpf-binding-issues-in-context-menu.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>分析现有 WPF / Windows Forms 程序能否顺利迁移到 .NET Core 3.0（使用 .NET Core 3.0 Desktop API Analyzer ）</title>
        <description>&lt;p&gt;今年五月的 Build 大会上，微软说 .NET Core 3.0 将带来 WPF / Windows Forms 这些桌面应用的支持。当然，是通过 Windows 兼容包（Windows Compatibility Pack）实现的。为了提前检查你的程序是否能在未来跑在 .NET Core 3.0 上，微软在 2018年8月8日 推出了 .NET Core 3.0 Desktop API Analyzer，帮助你提前检查你的程序能有多容易迁移到 .NET Core 3.0&lt;/p&gt;

&lt;p&gt;本文将介绍其使用方法，并介绍 API 的逐步迁移方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;net-core-30-desktop-api-analyzer&quot;&gt;.NET Core 3.0 Desktop API Analyzer&lt;/h2&gt;

&lt;p&gt;你可以前往 GitHub 查看 .NET Core 3.0 Desktop API Analyzer 项目：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/dotnet-apiport-ui&quot;&gt;Microsoft/dotnet-apiport-ui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;去 release 标签下即可下载。当然，目前仅发布一个版本，你也可以点击以下链接直接下载：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/dotnet-apiport-ui/releases&quot;&gt;PortabilityAnalyzer.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载完后解压到任意目录即可运行。&lt;/p&gt;

&lt;h2 id=&quot;分析一个-wpf-程序&quot;&gt;分析一个 WPF 程序&lt;/h2&gt;

&lt;p&gt;第一个想到的，是分析目前已在商店发布的基于 .NET Framework 4.7 的 WPF 程序 &lt;a href=&quot;ms-windows-store://pdp/?productid=9P8LNZRNJX85&quot;&gt;标识符命名工具 - Whitman&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-21-36.png&quot; alt=&quot;分析 WPF 程序&quot; /&gt;&lt;br /&gt;
▲ 分析 WPF 程序&lt;/p&gt;

&lt;p&gt;其实这个目录下只有一点点程序集，所以分析起来很快的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-22-46.png&quot; alt=&quot;Whitman 的目录结构&quot; /&gt;&lt;br /&gt;
▲ Whitman 的目录结构&lt;/p&gt;

&lt;p&gt;选好后，点击 &lt;strong&gt;Analyze&lt;/strong&gt;，在 &lt;strong&gt;Analyzing…&lt;/strong&gt; 提示等待之后，即可在它指定的临时目录中找到分析结果文件：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Report saved in: 
C:\Users\walterlv\AppData\Local\Temp\PortabilityReport.xlsx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;竟然是一个 Excel 表格！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-25-13.png&quot; alt=&quot;Excel 表格表示的结果&quot; /&gt;&lt;br /&gt;
▲ Excel 表格表示的结果&lt;/p&gt;

&lt;p&gt;可以看到，我的 Whitman 对 .NET Core 3.0 的 API 是 100% 兼容的。将来迁移的时候可以不需要修改代码。&lt;/p&gt;

&lt;h2 id=&quot;分析更复杂的程序&quot;&gt;分析更复杂的程序&lt;/h2&gt;

&lt;p&gt;我试着分析一个更庞大的 WPF 软件目录后，发现还是有一些 API 是不兼容的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-37-24.png&quot; alt=&quot;有一些 API 不兼容&quot; /&gt;&lt;br /&gt;
▲ 有一些 API 不兼容&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-39-08.png&quot; alt=&quot;有一些程序集兼容性很低&quot; /&gt;&lt;br /&gt;
▲ 有一些程序集兼容性很低&lt;/p&gt;

&lt;p&gt;这份 Excel 表格中还包含了具体哪些 API 是不兼容的，并为部分使用提供了建议：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-13-20-43-07.png&quot; alt=&quot;查看不兼容的 API&quot; /&gt;&lt;br /&gt;
▲ 查看不兼容的 API&lt;/p&gt;

&lt;p&gt;所以，我们只需要查找对对应 API（第一列）的使用，然后通过其他技术手段将其替换成别的方法来写即可解决这样的兼容性问题。&lt;/p&gt;

&lt;h2 id=&quot;着手解决兼容性问题&quot;&gt;着手解决兼容性问题&lt;/h2&gt;

&lt;p&gt;比如我们拿出其中一行：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Target type&lt;/th&gt;
      &lt;th&gt;Target member&lt;/th&gt;
      &lt;th&gt;Header for assembly name entries&lt;/th&gt;
      &lt;th&gt;.NET Core&lt;/th&gt;
      &lt;th&gt;Recommended changes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;T:System.Runtime.Remoting.Messaging.MethodCallMessageWrapper&lt;/td&gt;
      &lt;td&gt;T:System.Runtime.Remoting.Messaging.MethodCallMessageWrapper&lt;/td&gt;
      &lt;td&gt;Walterlv.Placeholder&lt;/td&gt;
      &lt;td&gt;Not supported&lt;/td&gt;
      &lt;td&gt;Remove usage.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;我们通过在 Walterlv.Placeholder（这只是个占位程序集，实际名称已隐去）中全解决方案中搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;MethodCallMessageWrapper&lt;/code&gt; 可以找到此 API 的所有使用。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMessage&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMessage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MethodCallMessageWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMethodCallMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略其他代码。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此方法在此处上下文的目的是实现 AOP 代理，即为了实现切面编程，允许在实体类的每个方法执行之前注入一些代码。&lt;/p&gt;

&lt;p&gt;既然此处基于 .NET Framework &lt;code class=&quot;highlighter-rouge&quot;&gt;MethodCallMessageWrapper&lt;/code&gt; 的 AOP 已不可用，那么我们需要寻找到 .NET Core 中 AOP 的替代品。例如 .NET Core 官方推荐的是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnetcore/AspectCore-Framework&quot;&gt;dotnetcore/AspectCore-Framework: AspectCore is an AOP-based cross platform framework for .NET Standard.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是，我们几乎需要改造此类型，使其对 .NET Framework 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;MethodCallMessageWrapper&lt;/code&gt; 的使用替换成对 &lt;code class=&quot;highlighter-rouge&quot;&gt;AspectCore-Framework&lt;/code&gt; 的依赖。&lt;/p&gt;

&lt;p&gt;这是一项繁重的工作，不过还是要做的。迁移到 .NET Core 有很多好处，不是吗？&lt;/p&gt;

&lt;h2 id=&quot;一些错误&quot;&gt;一些错误&lt;/h2&gt;

&lt;p&gt;额外的，在其他一些程序的分析中，我遇到了一些错误。通过混淆的比较，我认为此错误可能源于程序集的混淆：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unable to analyze.
Details:
Detecting assembly references                      [Failed]

Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
Cannot locate assembly information for System.Object. Microsoft assemblies found are:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你想了解更多混淆相关的资料，可以阅读我的另一篇博客：&lt;a href=&quot;/post/obfuscation-configurations-of-smart-assembly&quot;&gt;.NET 中各种混淆（Obfuscation）的含义、原理、实际效果和不同级别的差异（使用 SmartAssembly）&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;未来的迁移&quot;&gt;未来的迁移&lt;/h2&gt;

&lt;p&gt;.NET Core 并不会原生提供 WPF / Windows Forms 这些桌面应用的支持，而是通过 Windows 兼容包（Windows Compatibility Pack）实现。你可以阅读微软官方博客了解：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2017/11/16/announcing-the-windows-compatibility-pack-for-net-core/&quot;&gt;Announcing the Windows Compatibility Pack for .NET Core - .NET Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;迁移到 .NET Core 并不会为这些程序带来跨平台特性，只是能够充分利用到 .NET Core 带来的诸多好处而已。比如更高的性能，更方便的部署，及时的更新。当然还有 MIT 开源，我们能够和社区一起修复 Bug。&lt;/p&gt;

&lt;p&gt;关于 .NET Framework 迁移到 .NET Core 的好处，以及 .NET Framework 未来的支持情况，可以阅读微软的另一篇博客了解：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2018/10/04/update-on-net-core-3-0-and-net-framework-4-8/&quot;&gt;Update on .NET Core 3.0 and .NET Framework 4.8 - .NET Blog&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Oct 2018 22:40:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/net-core-desktop-api-analyzer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/net-core-desktop-api-analyzer.html</guid>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅（解析篇）</title>
        <description>&lt;p&gt;OPML 全称是 &lt;strong&gt;Outline Processor Markup Language&lt;/strong&gt; ，即 &lt;strong&gt;大纲处理标记语言&lt;/strong&gt;。目前流行于收集博客的 RSS 源，便于用户转移自己的订阅项目。&lt;/p&gt;

&lt;p&gt;本文将介绍这个古老的格式，并提供一个 .NET 上的简易解析器。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是两个部分的第二篇，前者是理解 OPML 格式，此篇是解析此格式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/using-opml-for-rss-migrating&quot;&gt;概念篇&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/deserialize-opml-using-dotnet&quot;&gt;解析篇（本文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;opml-格式&quot;&gt;OPML 格式&lt;/h2&gt;

&lt;p&gt;在解析之前，最好先理解此格式的的元素组成和元素属性，所以如果你没有阅读 &lt;a href=&quot;/post/using-opml-for-rss-migrating&quot;&gt;概念篇&lt;/a&gt;，请先前往阅读。&lt;/p&gt;

&lt;h2 id=&quot;创建适用于-rss-的简易-opml-模型&quot;&gt;创建适用于 RSS 的简易 OPML 模型&lt;/h2&gt;

&lt;p&gt;我们先为模型创建基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;OpmlModel&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;为了方便在客户端应用中使用，可以使其继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;INotifyPropertyChanged&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Rssman.Models&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OpmlModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotificationObject&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Rssman.Models&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NotificationObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotifyPropertyChanged&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyChangedEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyPropertyChangedInvocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallerMemberName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyPropertyChangedInvocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotifyPropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallerMemberName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;拿出我们关心的 &lt;code class=&quot;highlighter-rouge&quot;&gt;outline&lt;/code&gt; 的属性来解析，于是有：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Rssman.Models&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerDisplay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RssOutline {Text,nq}, {XmlUrl,nq}, Count={Children.Count,nq}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RssOutline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpmlModel&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutlineType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_xmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_htmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutlineType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XmlUrl&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_xmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_xmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HtmlUrl&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_htmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_htmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HasChildren&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObservableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObservableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 等待编写解析代码。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还有表示 OPML 文档的模型：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Rssman.Models&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DebuggerDisplay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RssOpml {Title,nq}, Count={Children.Count,nq}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RssOpml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpmlModel&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObservableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObservableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 等待编写解析代码。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;从-opml-文档中解析出模型&quot;&gt;从 OPML 文档中解析出模型&lt;/h2&gt;

&lt;p&gt;在以上的模型代码中，我为基类留有 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDeserializing&lt;/code&gt; 方法以供反序列化。&lt;/p&gt;

&lt;p&gt;为了尽可能简化此博客的代码，参数我直接使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;XElement&lt;/code&gt; 类型，以便在方法中使用 XPath 语法来解析。（当然，如果你是做库或者进行大型可维护项目的开发，这里就需要一些抽象了。）&lt;/p&gt;

&lt;p&gt;现在，我们写一个新的静态类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;Opml&lt;/code&gt; 来解析 OPML 文档：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Rssman.Services&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Opml&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RssOpml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ParseAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoadOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;XPathSelectElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;opml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RssOpml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;opml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，再补全模型 &lt;code class=&quot;highlighter-rouge&quot;&gt;RssOpml&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;RssOutline&lt;/code&gt; 的反序列化部分：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// RssOpml.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;XPathSelectElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;head/title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;XPathSelectElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;body/outline&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// RssOutline.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnDeserializing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutlineType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlineType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlineType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;xmlUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;XmlUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;htmlUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;htmlUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HtmlUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;htmlUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;XPathSelectElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;outline&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RssOutline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，以上两个方法请分别填充到 &lt;code class=&quot;highlighter-rouge&quot;&gt;RssOpml.cs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;RssOutline.cs&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDeserializing&lt;/code&gt; 方法中。&lt;/p&gt;

&lt;p&gt;这里，所有的 XML 解析均使用的是 XPath 语法，关于 XPath 语法，可以阅读 &lt;a href=&quot;/post/xml-xpath&quot;&gt;XML 的 XPath 语法 - walterlv&lt;/a&gt;，关于如何使用 XPath 在 .NET 中读写 XML 文件，可以阅读 &lt;a href=&quot;/post/read-write-xml-using-xpath-in-dotnet&quot;&gt;.NET 使用 XPath 来读写 XML 文件 - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;使用此-opml-模型&quot;&gt;使用此 OPML 模型&lt;/h2&gt;

&lt;p&gt;当你把这些类都准备好，那么你就可以使用简单的几句话来完成 OPML 文档的解析了。&lt;/p&gt;

&lt;p&gt;在 UWP 应用中，可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;StorageFile&lt;/code&gt; 来打开一个文件流：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstalledLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OpenStreamForReadAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sample-opml.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Opml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParseAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 使用此 OPML 文档&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 .NET Framework 传统应用中，可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;File.Read&lt;/code&gt; 来打开一个文件流。&lt;/p&gt;

&lt;p&gt;由于我们本文中创建的模型均实现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;INotifyPropertyChanged&lt;/code&gt; 接口，所以你甚至可以直接将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Opml.ParseAsync&lt;/code&gt; 的返回结果应用于绑定。&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Sep 2018 12:01:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/deserialize-opml-using-dotnet.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/deserialize-opml-using-dotnet.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>Git 更安全的强制推送，--force-with-lease</title>
        <description>&lt;p&gt;由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;git rebase&lt;/code&gt; 命令的存在，强制将提交推送到远端仓库似乎也有些必要。不过都知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;git push --force&lt;/code&gt; 是不安全的，这让 &lt;code class=&quot;highlighter-rouge&quot;&gt;git rebase&lt;/code&gt; 命令显得有些鸡肋。&lt;/p&gt;

&lt;p&gt;本文将推荐 &lt;code class=&quot;highlighter-rouge&quot;&gt;--force-with-lease&lt;/code&gt; 参数，让我们可以更安全地进行强制推送。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--force-with-lease&lt;/code&gt; 参数自 Git 的 1.8.5 版本开始提供，只在解决 &lt;code class=&quot;highlighter-rouge&quot;&gt;git push --force&lt;/code&gt; 命令造成的安全问题。&lt;/p&gt;

&lt;h3 id=&quot;那么-git-push---force-命令有什么安全问题&quot;&gt;那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;git push --force&lt;/code&gt; 命令有什么安全问题？&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--force&lt;/code&gt; 会使用本地分支的提交覆盖远端推送分支的提交。也就是说，如果其他人在相同的分支推送了新的提交，你的这一举动将“删除”他的那些提交！就算在强制推送之前先 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 并且 &lt;code class=&quot;highlighter-rouge&quot;&gt;merge&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt; 了也是不安全的，因为这些操作到推送之间依然存在时间差，别人的提交可能发生在这个时间差之内。&lt;/p&gt;

&lt;p&gt;如果你对这样的危险没有什么直观的感觉，可以看看这则新闻：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://my.oschina.net/javayou/blog/2206650&quot;&gt;还在用 Git 的 -f 参数强推仓库，你这是在作死！&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/WwQPn_881H3Knen7KVqsxw&quot;&gt;因代码不规范，码农枪击4名同事，一人情况危急&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-23-12-31-26.png&quot; alt=&quot;git push -f 致使枪杀&quot; /&gt;&lt;br /&gt;
▲ git push -f 致使枪杀&lt;/p&gt;

&lt;h3 id=&quot;--force-with-lease-将解决这种安全问题&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--force-with-lease&lt;/code&gt; 将解决这种安全问题&lt;/h3&gt;

&lt;p&gt;使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;--force-with-lease&lt;/code&gt; 参数之后，上面那种安全问题就没有那么危险了。&lt;/p&gt;

&lt;p&gt;使用此参数推送，如果远端有其他人推送了新的提交，那么推送将被拒绝，这种拒绝和没有加 &lt;code class=&quot;highlighter-rouge&quot;&gt;--force&lt;/code&gt; 参数时的拒绝是一样的。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;--force-with-lease&lt;/span&gt;
To https://github.com/walterlv/walterlv.github.io.git
 &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;rejected]        master -&amp;gt; master &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;fetch first&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
error: failed to push some refs to &lt;span class=&quot;s1&quot;&gt;'https://github.com/walterlv/walterlv.github.io.git'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;请特别注意&lt;/strong&gt;——如果你 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 之后在本地的 origin 相关分支上已经看到了别人的提交，依然进行强制推送，你还是会覆盖别人的提交。也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;--force-with-lease&lt;/code&gt; &lt;strong&gt;解决的是本地仓库不够新时，依然覆盖了远端新仓库的问题&lt;/strong&gt;，如果你&lt;strong&gt;执意想要覆盖远端提交&lt;/strong&gt;，只需要先 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 再推送，&lt;strong&gt;它也不会拒绝的&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;git push --force-with-lease&lt;/code&gt; 命令被拒绝时，你需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 仓库，然后确认其他人是否对此分支有新的修改，如果没有，你才可以继续强制推送。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git fetch
remote: Counting objects: 46, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Compressing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;29/29&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Total 46 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 21&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 40 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 15&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, pack-reused 0
Unpacking objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;46/46&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
From https://github.com/walterlv/walterlv.github.io
   e75edf0..217a49d  master     -&amp;gt; origin/master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 完毕之后，请一定检查此分支是否已经被其他人修改，如果有新的提交，你应该进行一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;merge&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git rebase
First, rewinding &lt;span class=&quot;nb&quot;&gt;head &lt;/span&gt;to replay your work on top of it...
Applying: Add post &lt;span class=&quot;s2&quot;&gt;&quot;safe push using force with lease&quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;此后，再次进行推送或强制推送即可。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;--force-with-lease&lt;/span&gt;
Counting objects: 4, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Delta compression using up to 8 threads.
Compressing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/4&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Writing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/4&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, 363 bytes | 363.00 KiB/s, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Total 4 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 3&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
remote: Resolving deltas: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3/3&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, completed with 3 &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;objects.
To https://github.com/walterlv/walterlv.github.io.git
   219a6d5..dff94a5  master -&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;额外的问题为什么推送到远端的提交还依然要用-rebase&quot;&gt;额外的问题：为什么推送到远端的提交还依然要用 rebase？&lt;/h3&gt;

&lt;p&gt;Git 官方文档对 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt; 有如下描述：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-07-18-58-13.png&quot; alt=&quot;Git 官方对 rebase 的描述&quot; /&gt;&lt;br /&gt;
▲ 如果你想吐槽那段中文翻译，我只想说——那是 Git 的官方中文文档&lt;/p&gt;

&lt;p&gt;既然已经推送的提交不应该再进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt;，那本不应该会遇到本文提到的问题。但是——GitHub 的工作流或者 GitLab 的工作流中，都有一种行为是 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt; 自己的分支到 &lt;code class=&quot;highlighter-rouge&quot;&gt;origin/master&lt;/code&gt; 上，以保证 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支上的提交是纯粹的干净的。也就是说，本意是禁止对合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支上的提交进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt;；但对于自己的 &lt;code class=&quot;highlighter-rouge&quot;&gt;temp&lt;/code&gt; 分支或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支，因为提交还没有合并到主干中，随时删除掉或者将历史进行美化也不会造成太大的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-07-19-13-29.png&quot; alt=&quot;GitLab 那种要求进行 rebase 的设置&quot; /&gt;&lt;br /&gt;
▲ 这是 GitLab 上的设置，可以要求提交者必须进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt; 才允许合并&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-push&quot;&gt;Git - git-push Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/5509543/6233938&quot;&gt;How do I properly force a Git push? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://weiqingtoh.github.io/force-with-lease/&quot;&gt;Force-with-lease: an alternative to force push - Weiqing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.atlassian.com/blog/2015/04/force-with-lease/&quot;&gt;–force considered harmful; understanding git’s –force-with-lease - Atlassian Developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 23 Sep 2018 04:33:38 +0000</pubDate>
        <link>https://blog.walterlv.com/post/safe-push-using-force-with-lease.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/safe-push-using-force-with-lease.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>再也不用克隆多个仓库啦！git worktree 一个 git 仓库可以连接多个工作目录</title>
        <description>&lt;p&gt;我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支开发得多些，但总时不时被高优先级的 BUG 打断需要临时去 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分一个分支出来解 BUG。git 2.6 以上开始提供了 worktree 功能，可以解决这样的问题。&lt;/p&gt;

&lt;p&gt;阅读本文将了解使用 git worktree 高效进行并行开发的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;git worktree 从一个仓库中可以创建多个工作目录，方便多开编辑器并行开发。&lt;/p&gt;

&lt;h2 id=&quot;快速上手&quot;&gt;快速上手&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git worktree add &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; &amp;lt;新分支名&amp;gt; &amp;lt;新路径&amp;gt; &amp;lt;从此分支创建&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如，你正在某个 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支开发，希望从 master 分出一个分支来解决某个紧急的 BUG：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git worktree add &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; t/walterlv/bugfix-100 ../Demo.bugfix master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，原本的仓库文件夹的同级目录下会出现一个 Demo.bugfix 文件夹（当然名字随便取）。这个仓库里只有一个 .git 文件用来记录这是主仓库的一个工作目录。&lt;/p&gt;

&lt;p&gt;自此，这两个工作目录在工作上看起来就像两个独立的仓库一样，都可以运行各种命令，包括切换分支。&lt;/p&gt;

&lt;p&gt;另外，你也可以不使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;-b&lt;/code&gt;，以便直接使用现有的分支，而不创建新的分支：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git worktree add &amp;lt;新路径&amp;gt; &amp;lt;从此分支创建&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如，你正在某个 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支开发，希望回到 master 分支解决某个紧急的 BUG：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git worktree add ../Demo.bugfix master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;相比于克隆多个仓库，使用这种方法创建的多个目录，有诸多好处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;只有一个仓库会占用版本库的空间，其它只占用工作目录的空间，对大型项目而言非常节省空间。&lt;/li&gt;
  &lt;li&gt;因为所有工作目录共享一个仓库，所以一个更新意味着整个更新（A 目录里对分支做的改动，B 目录里切到此分支也是改动后的；避免到时候找不到某个未推送的改动改到了哪个仓库）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;

&lt;p&gt;使用 git worktree 创建的多个目录，不能有任何两个目录在同一个分支下——原因应该不言自明。&lt;/p&gt;

&lt;p&gt;如果要删除其中一个工作目录，直接删除文件夹即可。随后使用命令清除多余的已经被删的工作目录：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git worktree prune
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 17 Sep 2018 10:45:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git-worktree.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git-worktree.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>.NET 命令行参数包含应用程序路径吗？</title>
        <description>&lt;p&gt;如果你关注过命令行参数，也许发现有时你会在命令行参数的第一个参数中中看到应用程序的路径，有时又不会。那么什么情况下有路径呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;其实是否有路径只是取决于获取命令行参数的时候用的是什么方法。而这是 Windows 操作系统的机制，与具体的运行环境无关。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;测试程序&quot;&gt;测试程序&lt;/h2&gt;

&lt;p&gt;考虑下面这样的测试程序：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Globalization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.CommandLines&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;参数总数：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OutputArgsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;按任意键继续……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutputArgsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当我们向命令行中传入参数的时候，我们可以得到所有的命令行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-11-21-13-55.png&quot; alt=&quot;Main 函数中的命令行参数&quot; /&gt;&lt;br /&gt;
▲ Main 函数中的命令行参数&lt;/p&gt;

&lt;p&gt;这种行为与具体的 .NET SDK 无关。看我们的项目文件，可以发现，无论是老旧的 .NET Framework 4.5 还是新的 .NET Framework 4.7.2 还是更加主流的 .NET Core 2.1，命令行参数中都是没有应用程序路径的。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net45;net472;netcoreapp2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那为什么有时候会看到应用程序路径呢？&lt;/p&gt;

&lt;h2 id=&quot;解释&quot;&gt;解释&lt;/h2&gt;

&lt;p&gt;在《Windows 核心编程》一书中有说到：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;可以获得一个指向进程的完整命令行的指针，方法是调用 GetCommandLine 函数：&lt;/p&gt;
  &lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;PTSTR&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
  &lt;p&gt;该函数返回一个指向包含完整命令行的缓存的指针，该命令行包括执行文件的完整路径名。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCommandLine&lt;/code&gt; 函数时，我们将得到包含执行文件的完整路径名的命令行参数。这个方法对应到 .NET 中，是 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Environment.GetCommandLineArgs()&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;于是修改我们刚刚的函数，加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Environment.GetCommandLineArgs()&lt;/code&gt; 的调用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Globalization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.CommandLines&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Main 函数参数列表中参数总数：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OutputArgsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCommandLineArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;GetCommandLineArgs 参数总数：&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OutputArgsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;按任意键继续……&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutputArgsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PadLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们能看到参数列表中多了应用程序的完整路径：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-11-21-22-43.png&quot; alt=&quot;GetCommandLineArgs 中的命令行参数&quot; /&gt;&lt;br /&gt;
▲ GetCommandLineArgs 中的命令行参数&lt;/p&gt;

&lt;p&gt;事实上这样的差异不止在 .NET 中有体现，整个 Windows 上的程序都是这样的特性。这在《Windows 核心编程》一书中是有说明的。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Main 函数的参数中不包含应用程序执行路径；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Environment.GetCommandLineArgs()&lt;/code&gt; 得到的命令行参数中包含应用程序的执行路径；&lt;/li&gt;
  &lt;li&gt;Windows 上的所有程序其命令行参数的行为表现都是如此，这不是 .NET 的专属特性。&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Thu, 13 Sep 2018 03:24:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/when-will-the-command-line-args-contain-the-executable-path.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/when-will-the-command-line-args-contain-the-executable-path.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>WPF 同一窗口内的多线程 UI（VisualTarget）</title>
        <description>&lt;p&gt;WPF 的 UI 逻辑只在同一个线程中，这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI，大家也会想到使用另一个窗口来实现，让每个窗口拥有自己的 UI 线程。然而，就不能让同一个窗口内部使用多个 UI 线程吗？&lt;/p&gt;

&lt;p&gt;阅读本文将收获一份对 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 的解读以及一份我封装好的跨线程 UI 控件 &lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/DispatcherContainer.cs&quot;&gt;DispatcherContainer.cs&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;WPF 同一个窗口中跨线程访问 UI 有多种方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/multi-thread-ui-using-visualtarget-in-wpf&quot;&gt;使用 VisualTarget (本文)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/embed-win32-window-using-csharp&quot;&gt;使用 SetParent 嵌入另一个窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前者使用的是 WPF 原生方式，做出来的跨线程 UI 可以和原来的 UI 相互重叠遮挡。后者使用的是 Win32 的方式，实际效果非常类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowsFormsHost&lt;/code&gt;，新线程中的 UI 在原来的所有 WPF 控件上面遮挡。另外，后者不止可以是跨线程，还可以跨进程。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;几个必备的组件&quot;&gt;几个必备的组件&lt;/h2&gt;

&lt;p&gt;微软给 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 提供的注释是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;提供跨线程边界将一个可视化树连接到另一个可视化树的功能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;注释中说 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 就是用来连接可视化树（&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTree&lt;/code&gt;）的，而且可以跨线程边界。也就是说，这是一个专门用来使同一个窗口内部包含多个不同 UI 线程的类型。&lt;/p&gt;

&lt;p&gt;所以，我们的目标是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 显示跨线程边界的 UI。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 本身继承自 &lt;code class=&quot;highlighter-rouge&quot;&gt;CompositionTarget&lt;/code&gt;，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;；其本身并不是可视化树的一部分。但是它的构造函数中可以传入一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;HostVisual&lt;/code&gt; 对象，这个对象是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;，如果将此 &lt;code class=&quot;highlighter-rouge&quot;&gt;HostVisual&lt;/code&gt; 放入原 UI 线程的可视化树上，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 就与主 UI 线程连接起来了。&lt;/p&gt;

&lt;p&gt;另外一半，&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 需要连接另一个异步线程的可视化树。然而，&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;RootVisual&lt;/code&gt; 属性，直接给此属性赋一个后台 UI 控件作为其值，即连接了另一个 UI 线程的可视化树。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;总结起来&lt;/strong&gt;，其实我们只需要 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 的新实例，构造函数传入一个 UI 线程的可视化树中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;HostVisual&lt;/code&gt; 实例，&lt;code class=&quot;highlighter-rouge&quot;&gt;RootVisual&lt;/code&gt; 属性设置为另一个 UI 线程中的控件，即可完成跨线程可视化树的连接。&lt;/p&gt;

&lt;p&gt;事实上经过尝试，我们真的只需要这样做就可以让另一个线程上的 UI 呈现到当前的窗口上，同一个窗口。&lt;em&gt;读者可以自行编写测试代码验证这一点，我并不打算在这里贴上试验代码，因为后面会给出完整可用的全部代码。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;完善基本功能&quot;&gt;完善基本功能&lt;/h2&gt;

&lt;p&gt;虽说 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 的基本使用已经可以显示一个跨线程的 UI 了，但是其实功能还是欠缺的。&lt;/p&gt;

&lt;p&gt;一个典型的情况是，后台线程的这部分 UI 没有连接到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt;；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual.PointFromScreen&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Visual.PointFromScreen&lt;/code&gt; 这样的方法明确需要连接到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 才行。参见这里：&lt;a href=&quot;https://stackoverflow.com/questions/2154211/in-wpf-under-what-circumstances-does-visual-pointfromscreen-throw-invalidoperat&quot;&gt;In WPF, under what circumstances does Visual.PointFromScreen throw InvalidOperationException? - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可是，应该如何将 &lt;code class=&quot;highlighter-rouge&quot;&gt;RootVisual&lt;/code&gt; 连接到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 呢？我从 Microsoft.DwayneNeed 项目中找到了方法。这是源码地址：&lt;a href=&quot;http://microsoftdwayneneed.codeplex.com/&quot;&gt;Microsoft.DwayneNeed - Home&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;做法是重写属性和方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Visual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_visualTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 此处省略大量代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CompositionTarget&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCompositionTargetCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_visualTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://microsoftdwayneneed.codeplex.com/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.DwayneNeed&lt;/code&gt;&lt;/a&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTargetPresentationSource&lt;/code&gt; 的完整代码，我自己只为这个类添加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt; 接口，用于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTarget&lt;/code&gt; 的实例。我需要这么做是因为我即将提供可修改后台 UI 线程控件的方法。&lt;/p&gt;

&lt;h2 id=&quot;让方法变得好用&quot;&gt;让方法变得好用&lt;/h2&gt;

&lt;p&gt;为了让整个多线程 UI 线程的使用行云流水，我准备写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherContainer&lt;/code&gt; 类来优化多线程 UI 的使用体验。期望的使用方法是给这个控件的实例设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Child&lt;/code&gt; 属性，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Child&lt;/code&gt; 是后台线程创建的 UI。然后一切线程同步相关的工作全部交给此类来完成。&lt;/p&gt;

&lt;p&gt;在我整理后，使用此控件只需如此简单：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#FFEEEEEE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;local:DispatcherContainer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Host&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetChildAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyUserControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;MyUserControl&lt;/code&gt; 是控件的类型，可以是你写的某个 XAML 用户控件，也可以是其他任何控件类型。用这个方法创建的控件，直接就是后台 UI 线程的。&lt;/p&gt;

&lt;p&gt;当然，如果你需要自己控制初始化逻辑，可以使用委托创建控件。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetChildAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextBox&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;吕毅 - walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Margin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Thickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下图即是用以上代码创建的后台线程文本框。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-30-23-16-46.png&quot; alt=&quot;后台线程的文本框&quot; /&gt;&lt;/p&gt;

&lt;p&gt;甚至，你已经有线程的后台 UI 控件了，或者你希望自己来创建后台的 UI 控件，则可以这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 创建一个后台线程的 Dispatcher。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其中，UIDispatcher 是我自己封装的方法，在 GitHub 上以 MIT 协议开源。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/UIDispatcher.cs&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunNewAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv's testing thread&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 使用这个后台线程的 Dispatcher 创建一个自己的用户控件。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;control&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyUserControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 将这个用户控件放入封装好的 DispatcherContainer 中。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// DispatcherContainer 是我自己封装的方法，在 GitHub 上以 MIT 协议开源。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/DispatcherContainer.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetChildAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到我们自己创建的控件已经运行在后台线程中了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-30-23-24-39.png&quot; alt=&quot;运行在后台线程中&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;完整的代码&quot;&gt;完整的代码&lt;/h2&gt;

&lt;p&gt;以下所有代码均可点击进入 GitHub 查看。&lt;/p&gt;

&lt;p&gt;核心的代码包含两个类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/VisualTargetPresentationSource.cs&quot;&gt;VisualTargetPresentationSource&lt;/a&gt; 这是实现异步 UI 的关键核心，用于连接两个跨线程边界的可视化树，并同时提供连接到 &lt;code class=&quot;highlighter-rouge&quot;&gt;PresentationSource&lt;/code&gt; 的功能。（由于我对 PresentationSource 的了解有限，此类绝大多数代码都直接来源于 &lt;a href=&quot;http://microsoftdwayneneed.codeplex.com/&quot;&gt;Microsoft.DwayneNeed - Home&lt;/a&gt;。）&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/DispatcherContainer.cs&quot;&gt;DispatcherContainer&lt;/a&gt; 当使用我封装好的多线程 UI 方案时（其实就是把这几个类自己带走啦），这个类才是大家编程开发中主要面向的 API 类啊！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其他辅助型代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Threading/UIDispatcher.cs&quot;&gt;UIDispatcher&lt;/a&gt; 这并不是重点，此类型只是为了方便地创建后台 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.Sharing/Utils/Threading/DispatcherAsyncOperation.cs&quot;&gt;DispatcherAsyncOperation&lt;/a&gt; 此类型只是为了让 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIDispatcher&lt;/code&gt; 中的方法更好写一些。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Core/Threading/AwaiterInterfaces.cs&quot;&gt;AwaiterInterfaces&lt;/a&gt; 这是一组可有可无的接口；给 &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatcherAsyncOperation&lt;/code&gt; 继承的接口，但是不继承也没事，一样能跑。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些辅助型代码的含义可以查看我的另一篇博客：&lt;a href=&quot;/post/write-custom-awaiter&quot;&gt;如何实现一个可以用 await 异步等待的 Awaiter - walterlv&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blogs.interknowlogy.com/2014/12/03/wpf-round-table-part-2-multi-ui-threaded-control/&quot;&gt;WPF Round Table Part 2: Multi UI Threaded Control - //InterKnowlogy/ Blogs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dwayneneed/2007/04/26/multithreaded-ui-hostvisual/&quot;&gt;Multithreaded UI: HostVisual – Presentation Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://microsoftdwayneneed.codeplex.com/&quot;&gt;Microsoft.DwayneNeed - Home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 05 Sep 2018 05:47:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/multi-thread-ui-using-visualtarget-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/multi-thread-ui-using-visualtarget-in-wpf.html</guid>
        
        
        <category>wpf</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 推荐一个我设计的缓存类型（适合缓存反射等耗性能的操作，附用法）</title>
        <description>&lt;p&gt;这里我想说的是类型“实例”的缓存，适用于那些实例或者值计算很耗时的操作。典型的场景如反射获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;适用&quot;&gt;适用&lt;/h2&gt;

&lt;p&gt;本文推荐的方法适用于相同的输入可以获得相同的输出，但是这个输入到输出的过程非常耗时。&lt;/p&gt;

&lt;p&gt;大家都知道反射是很耗时的，尤其是获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 和反射调用实例的方法。而从一个反射的成员中得到其 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 是唯一的输入对应唯一的输出。&lt;/p&gt;

&lt;h2 id=&quot;思路&quot;&gt;思路&lt;/h2&gt;

&lt;p&gt;既然唯一的输入对应唯一的输出，那么我们可以通过一个字典来储存我们已经转换过的输出。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 其中 TSource 表示输入的类型，TCache 表示输出的类型。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cacheDictionary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后我们把已经计算过输出的输入存入到这个字典中。这样，当我们试图重新计算相同输入的输出的时候，便可以直接从字典中取得所需的输出的值。&lt;/p&gt;

&lt;p&gt;为了通用一点，我设计一个类型 &lt;code class=&quot;highlighter-rouge&quot;&gt;CachePool&amp;lt;TSource, TCache&amp;gt;&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CachePool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cacheDictionary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetOrCacheValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 从这里计算新值或者从字典中获取已经计算的值。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个计算过程是唯一确定的，所以我们可以从构造函数中传入并储存下来。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CachePool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conversion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_convert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conversion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conversion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们的缓存类已经近乎完成。为了线程安全，我加了锁；但考虑到部分情况下性能更重要，所以我把锁设为了可选项。&lt;/p&gt;

&lt;h2 id=&quot;代码&quot;&gt;代码&lt;/h2&gt;

&lt;p&gt;代码我放到了 &lt;a href=&quot;https://gist.github.com/walterlv&quot;&gt;gist.github.com&lt;/a&gt;，&lt;a href=&quot;https://gist.github.com/walterlv/85c43ce2c064e7a2bd2b70756b968cd5&quot;&gt;walterlv/CachePool.cs&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;你可以直接点击以上链接查看。为了不影响本文的阅读，我把实际的代码放到了最后。&lt;/p&gt;

&lt;h2 id=&quot;用法&quot;&gt;用法&lt;/h2&gt;

&lt;h3 id=&quot;高性能创建对象&quot;&gt;高性能创建对象&lt;/h3&gt;

&lt;p&gt;比如你认为反射创建对象是一个耗时的操作，那么可以将构造函数的调用创建成一个委托，然后把这个委托缓存下来。这样，下次创建相同对象的时候就不需要反射调用构造函数了，而是直接调用委托拿到对象的新实例。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachePool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConstructorCache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachePool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;高性能为属性赋值&quot;&gt;高性能为属性赋值&lt;/h3&gt;

&lt;p&gt;我在 &lt;a href=&quot;/post/how-to-quickly-write-emit-code&quot;&gt;如何快速编写和调试 Emit 生成 IL 的代码&lt;/a&gt; 一文中创建了可以为属性赋值的委托，你也可以使用此方法将委托缓存下来，以便每次给相同类型的相同属性赋值时能有不那么差的性能。&lt;/p&gt;

&lt;h3 id=&quot;高性能反射调用函数&quot;&gt;高性能“反射”调用函数&lt;/h3&gt;

&lt;p&gt;调用函数所得的结果可是不一样的，所以直接缓存函数结果是不靠谱的，不过我们依然可以将反射调用缓存为委托的调用。我在 &lt;a href=&quot;/post/create-delegate-to-improve-reflection-performance&quot;&gt;.NET Core/Framework 创建委托以大幅度提高反射调用的性能&lt;/a&gt; 一文中有介绍。&lt;/p&gt;

&lt;h2 id=&quot;附代码&quot;&gt;附代码&lt;/h2&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/85c43ce2c064e7a2bd2b70756b968cd5.js&quot;&gt;&lt;/script&gt;

</description>
        <pubDate>Sun, 02 Sep 2018 07:59:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/design-a-cache-pool.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/design-a-cache-pool.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用反射调用含 ref 或 out 参数的方法</title>
        <description>&lt;p&gt;使用反射，我们可以很容易地在运行时调用一些编译时无法确定的属性、方法等。然而，如果方法的参数中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;ref&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 关键字的时候，又该怎么调用呢？&lt;/p&gt;

&lt;p&gt;本文将介绍如何反射调用含 &lt;code class=&quot;highlighter-rouge&quot;&gt;ref&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 关键字的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;比如我们有这样的类型：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么反射的时候可以使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;key&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而现在我们的函数是这样的，带一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 关键字的参数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;事实上，无论是什么样的方法，在反射式调用的都是同一个方法，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ref&lt;/code&gt; 关键字的方法来说，会更新传入的数组，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; 最后传入的那个参数。所以其实我们只需要保存那个数组的实例，在调用完毕之后便能重新取出被修改的参数了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 在这里可以从 args 里面取出被 ref 或者 out 修改的参数。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/97728/out-ref-and-InvokeMember&quot;&gt;out, ref and InvokeMember !!! - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/8779731/6233938&quot;&gt;c# - How to pass a parameter as a reference with MethodInfo.Invoke - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 02 Sep 2018 06:59:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/handle-ref-or-out-arguments-using-reflection.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/handle-ref-or-out-arguments-using-reflection.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用反射注册事件</title>
        <description>&lt;p&gt;使用反射，我们可以很容易地在运行时调用一些编译时无法确定的属性、方法等。那么如何注册事件呢？&lt;/p&gt;

&lt;p&gt;本文将介绍如何使用反射注册事件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;不使用反射&quot;&gt;不使用反射&lt;/h2&gt;

&lt;p&gt;例如，我们希望反射的类型是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么只需要使用如下代码即可完成事件的注册：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Walterlv_BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv_BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用反射&quot;&gt;使用反射&lt;/h2&gt;

&lt;p&gt;而如果使用反射，则是：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv_BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walterlv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，实际使用的时候，如果能访问到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv&lt;/code&gt; 类型，当然也不会去用到反射，所以通常情况是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;安全地使用反射&quot;&gt;安全地使用反射&lt;/h2&gt;

&lt;p&gt;虽然以上方式使用了反射成功注册了事件，但实际上我们的参数中传入了一个特定类型的委托 &lt;code class=&quot;highlighter-rouge&quot;&gt;EventHandler&lt;/code&gt;。实际上事件的委托种类非常多。&lt;/p&gt;

&lt;p&gt;在委托中，即便签名完全相同，也不是同一个委托类型。如果传入的参数类型改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;EventHandler&amp;lt;EventArgs&amp;gt;&lt;/code&gt;，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;BlogPublished&lt;/code&gt; 事件的类型改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;EventHandler&amp;lt;EventHandler&amp;gt;&lt;/code&gt;，虽然实际上这两个委托的签名是兼容的，但其委托类型不同，依然是不能互相转换的。你会在运行时遇到一下异常：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-09-01-20-08-33.png&quot; alt=&quot;委托无法转换&quot; /&gt;&lt;br /&gt;
▲ 委托无法转换&lt;/p&gt;

&lt;p&gt;所以我们必须有一些更安全的方式来注册事件。&lt;/p&gt;

&lt;p&gt;正常情况下，我们转换一个签名兼容的委托是使用构造函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么在反射中，我们需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.CreateDelegate&lt;/code&gt; 创建指定类型的委托。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Walterlv_BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@delegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventHandlerType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walterlv_BlogPublished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;Delegate.CreateDelegate&lt;/code&gt; 的作用就是执行委托类型的转换。我在 &lt;a href=&quot;/post/create-delegate-to-improve-reflection-performance&quot;&gt;.NET Core/Framework 创建委托以大幅度提高反射调用的性能&lt;/a&gt; 中也提到过这个方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/1121489/6233938&quot;&gt;c# - AddEventHandler using reflection - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 12:31:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-event-handler-using-reflection.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-event-handler-using-reflection.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 判断某个类是否是泛型类型或泛型接口的子类型</title>
        <description>&lt;p&gt;.NET 中提供了很多判断某个类型或实例是某个类的子类或某个接口的实现类的方法，然而这事情一旦牵扯到泛型就没那么省心了。&lt;/p&gt;

&lt;p&gt;本文将提供判断泛型接口实现或泛型类型子类的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;net-中没有自带的方法&quot;&gt;.NET 中没有自带的方法&lt;/h2&gt;

&lt;p&gt;对于实例，.NET 中提供了这些方法来判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于类型，.NET 中提供了这些方法来判断：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsAssignableFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFoo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsAssignableFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者，如果不用判断接口，只判断类型的话：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsSubClassOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof&lt;/code&gt; 关键字，不止可以写 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo)&lt;/code&gt;，还可以写 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo&amp;lt;&amp;gt;)&lt;/code&gt;。这可以得到泛型版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&amp;lt;T&amp;gt;&lt;/code&gt; 的类型。&lt;/p&gt;

&lt;p&gt;不过，如果你试图拿这个泛型版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo&amp;lt;&amp;gt;)&lt;/code&gt; 执行上述所有判断，你会发现所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 条件都会是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;我们需要自己编写方法&quot;&gt;我们需要自己编写方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo&amp;lt;&amp;gt;)&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo&amp;lt;SomeClass&amp;gt;)&lt;/code&gt; 之间的关系就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetGenericTypeDefinition&lt;/code&gt; 函数带来的关系。&lt;/p&gt;

&lt;p&gt;所以我们可以充分利用这一点完成泛型类型的判断。&lt;/p&gt;

&lt;p&gt;比如，我们要判断接口：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasImplementedRawGeneric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 遍历类型实现的所有接口，判断是否存在某个接口是泛型，且是参数中指定的原始泛型的实例。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInterfaces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetGenericTypeDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而如果需要判断类型，那么就需要遍历此类的基类了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSubClassOfRawGeneric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetGenericTypeDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，我们可以把这两个方法合成一个，用于实现类似 &lt;code class=&quot;highlighter-rouge&quot;&gt;IsAssignableFrom&lt;/code&gt; 的效果，不过这回将支持原始接口（也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(Foo&amp;lt;&amp;gt;)&lt;/code&gt;）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 判断指定的类型 &amp;lt;paramref name=&quot;type&quot;/&amp;gt; 是否是指定泛型类型的子类型，或实现了指定泛型接口。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;type&quot;&amp;gt;需要测试的类型。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;generic&quot;&amp;gt;泛型接口类型，传入 typeof(IXxx&amp;amp;lt;&amp;amp;gt;)&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;如果是泛型接口的子类型，则返回 true，否则返回 false。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasImplementedRawGeneric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 测试接口。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInterfaces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 测试类型。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 没有找到任何匹配的接口或类型。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 测试某个类型是否是指定的原始接口。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsTheRawGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsGenericType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetGenericTypeDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 08:28:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/is-subclass-of-raw-generic-or-implemented-raw-generic.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/is-subclass-of-raw-generic-or-implemented-raw-generic.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Win32 程序在启动时激活前一个启动程序的窗口</title>
        <description>&lt;p&gt;UWP 程序天生单实例。当然，新 API （10.0.17134）开始也提供了多实例功能。不过，传统 Win32 程序可就要自己来控制单实例了。&lt;/p&gt;

&lt;p&gt;本文介绍简单的几个 Win32 方法调用，使 Win32 程序也支持单实例。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;激活之前进程的窗口&quot;&gt;激活之前进程的窗口&lt;/h2&gt;

&lt;p&gt;我们可以通过进程名称找到此前已经启动过的进程实例，如果发现，就激活它的窗口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCurrentProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProcessesByName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainWindowHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;ShowWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 启动自己的主窗口，此部分代码省略。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShowWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCmdShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你一定觉得那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;9&lt;/code&gt; 很奇怪，它是多个不同的 nCmdShow 的值：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;0 &lt;code class=&quot;highlighter-rouge&quot;&gt;Hide&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;1 &lt;code class=&quot;highlighter-rouge&quot;&gt;Minimized&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;2 &lt;code class=&quot;highlighter-rouge&quot;&gt;Maximized&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;9 &lt;code class=&quot;highlighter-rouge&quot;&gt;Restore&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，找到的窗口此时可能并不处于激活状态。例如在 Windows 10 中，此窗口可能在其他桌面上。那么我们需要添加额外的代码将其显示出来。&lt;/p&gt;

&lt;p&gt;在前面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowWindow&lt;/code&gt; 之后，再调用一下 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetForegroundWindow&lt;/code&gt; 即可将其激活到最前面来。如果在其他桌面，则会切换到对应的桌面。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;USER32.DLL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetForegroundWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainWindowHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;ShowWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;SetForegroundWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;找到并激活窗口&quot;&gt;找到并激活窗口&lt;/h2&gt;

&lt;p&gt;以上方法适用于普通的主窗口。然而当窗口并不是进程的主窗口，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowInTaskBar&lt;/code&gt; 设为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 的时候就不生效了（此时窗口句柄会改变）。&lt;/p&gt;

&lt;p&gt;于是，我们需要改用其他的方式来查找窗口。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STAThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;那个窗口的标题栏文字&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;ShowWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 启动自己的主窗口，此部分代码省略。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lpWindowName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.c-sharpcorner.com/article/controlling-window-state-of-other-applications-using-C-Sharp/&quot;&gt;Controlling Window State Of Other Applications using C#&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/8935985/6233938&quot;&gt;c# - How to show/hide an application with Visible and ShowInTaskBar as false - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms633549%28VS.85%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;ShowWindowAsync function (Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bde4870-1599-4958-9ab4-902fa98ba53a/how-do-i-maximizeminimize-applications-programmatically-in-c?forum=csharpgeneral&quot;&gt;How do I maximize/minimize applications programmatically in C#?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:15:17 +0000</pubDate>
        <link>https://blog.walterlv.com/post/show-previous-process-instance-window-when-startup.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/show-previous-process-instance-window-when-startup.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Windows 无法删除文件夹 —— 访问被拒绝 / 因为目录不是空的</title>
        <description>&lt;p&gt;在日常使用 Windows 10 时，有时会遇到删除很普通的文件夹时提示“访问被拒绝”，以管理员权限重试后依然提示没有权限。如果使用命令行删除，则会提示“无法删除文件夹 XXX，目录不是空的。”。&lt;/p&gt;

&lt;p&gt;本文将介绍其原因并提供解决方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;删除文件夹遭到拒绝&quot;&gt;删除文件夹遭到拒绝&lt;/h2&gt;

&lt;p&gt;有时我们在删除一个很普通的文件夹时，会提示需要提升权限才能删除。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-13-15-38-13.png&quot; alt=&quot;需要提升权限&quot; /&gt;&lt;br /&gt;
▲ 需要提升权限&lt;/p&gt;

&lt;p&gt;其实按照经验，这种问题与权限并没有什么关系。尤其是以上这种 NuGet 缓存目录下的文件夹，和权限更是扯不上关系。&lt;/p&gt;

&lt;p&gt;所以其实点了“继续”也并没有什么左右，依然是没完没了的错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-13-15-40-53.png&quot; alt=&quot;需要访问权限&quot; /&gt;&lt;br /&gt;
▲ 需要访问权限&lt;/p&gt;

&lt;p&gt;如果我 &lt;strong&gt;一层层进入到文件夹的里面&lt;/strong&gt;，然后 &lt;strong&gt;先删除文件&lt;/strong&gt;，再一层层 &lt;strong&gt;退出来删掉文件夹&lt;/strong&gt;，那么这个文件夹就能被正常删除掉。&lt;/p&gt;

&lt;p&gt;这至少能说明，&lt;strong&gt;并没有文件或文件夹处于被占用的状态&lt;/strong&gt;！！！&lt;/p&gt;

&lt;p&gt;所以这个时候我考虑使用命令行删除：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-13-16-06-01.png&quot; alt=&quot;使用命令行删除&quot; /&gt;&lt;br /&gt;
▲ 使用命令行删除&lt;/p&gt;

&lt;p&gt;命令行删除时，给了一个错误提示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;rd : Directory C:\Users\lvyi.nuget\packages\walterlv.package.demo\12
.0.27-alpha\src\Demo_\MagicalDemo_\Magical_ cannot be removed
because it is not empty.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意思是说，命令行在删除其中一个子文件夹的时候出错，原因是：“目录不是空的。”&lt;/p&gt;

&lt;p&gt;如果继续翻看下面的错误提示，发现这是一个按文件夹递归的提示。&lt;/p&gt;

&lt;h2 id=&quot;解决方案&quot;&gt;解决方案&lt;/h2&gt;

&lt;p&gt;在网上搜索“目录不是空的”能得到不少结果，而且提供了不少解决方案：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/22948189/6233938&quot;&gt;windows - Batch - Getting “The directory is not empty” on rmdir command - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/38141528/6233938&quot;&gt;powershell - Cannot remove item. The directory is not empty - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;还有更多……&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而，无论敲入什么样的命令，都没有用。这时我抱着试一试的心态去搜索框（小娜）中搜索“资源监视器”或直接输入 resmon 命令打开资源监视器。在“关联的句柄”中我输入了无法删除的文件夹名称，才终于找到了根本原因：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-13-16-51-43.png&quot; alt=&quot;资源监视器&quot; /&gt;&lt;br /&gt;
▲ 资源监视器&lt;/p&gt;

&lt;p&gt;结束掉可能用到了这个版本 NuGet 包的 Visual Studio 后，文件夹可以被正常删除掉了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-13-16-58-19.png&quot; alt=&quot;删除文件夹&quot; /&gt;&lt;br /&gt;
▲ 因为删除太快，好不容易抓到的一张图&lt;/p&gt;

&lt;p&gt;所以什么“需要管理员权限”啊，什么“目录不是空的”，都是假的！！！真正的原因还是文件夹被占用。&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:14:59 +0000</pubDate>
        <link>https://blog.walterlv.com/post/delete-directory-that-is-not-empty.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/delete-directory-that-is-not-empty.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>在操作系统重启后恢复应用程序的工作状态</title>
        <description>&lt;p&gt;Windows 10 创意者更新之后，默认开启了重启后恢复应用程序状态的功能。这是自 Vista 以来就提供的功能——Restart Manager。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;应用程序实现这一功能只需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;RegisterApplicationRestart&lt;/code&gt; 即可。传入两个参数：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;重启后使用的命令行参数（例如当前正在打开的文件，以及正在阅读或编辑的位置）&lt;/li&gt;
  &lt;li&gt;决定是否进行重启的限制标记（任何时候都能重启还是在某些条件下关掉重启功能）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我封装了以下这个函数的调用并将其放到 GitHub 上：&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Windows/Interop/ApplicationRestartManager.cs&quot;&gt;sharing-demo/ApplicationRestartManager.cs at master · walterlv/sharing-demo&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;调用代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationRestartManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsRestartManagerSupported&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ApplicationRestartManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterApplicationRestart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;currentOpeningFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ApplicationRestartFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;附&lt;/strong&gt;：封装的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationRestartManager&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics.Contracts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Runtime.InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Jetbrains.Annotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Win32&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 为应用程序提供重启后恢复状态的功能。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationRestartManager&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 获取一个值，该值指示当前操作系统环境是否支持 Restart Manager 功能。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Restart Manager 是 Windows Vista 新增的功能。在 Windows 10 秋季创意者更新之后，默认开启了 EWX_RESTARTAPPS。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsSupported&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsSupportedLazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 向操作系统的 Restart Manager 注册应用终止后的重启方式。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;pwsCommandLine&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 应用程序的重启时应该使用的参数，允许为 null，表示不带参数。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 请注意：如果命令行参数中的某一个参数包含空格，请加上引号。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;dwFlags&quot;&amp;gt;为应用程序的重启行为添加限制，默认没有限制。&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterApplicationRestart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanBeNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwsCommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ApplicationRestartFlags&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwFlags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationRestartFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterApplicationRestart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pwsCommandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Windows Vista 及以上才开启 Restart Manager。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContractPublicPropertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsSupported&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsSupportedLazy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OSVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Registers the active instance of an application for restart.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// See also: [RegisterApplicationRestart function (Windows)](https://msdn.microsoft.com/en-us/library/windows/desktop/aa373347)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;pwzCommandline&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// A pointer to a Unicode string that specifies the command-line arguments for the application when it is restarted. The maximum size of the command line that you can specify is RESTART_MAX_CMD_LINE characters. Do not include the name of the executable in the command line; this function adds it for you.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// If this parameter is NULL or an empty string, the previously registered command line is removed. If the argument contains spaces, use quotes around the argument.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;dwFlags&quot;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// This parameter can be 0 or one or more of the following values:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - 1: Do not restart the process if it terminates due to an unhandled exception.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - 2: Do not restart the process if it terminates due to the application not responding.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - 4: Do not restart the process if it terminates due to the installation of an update.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - 8: Do not restart the process if the computer is restarted as the result of an update.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// This function returns S_OK on success or one of the following error codes.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - E_FAIL: Internal error.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// - E_INVALIDARG: The specified command line is too long.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterApplicationRestart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwzCommandline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 表示重启时的限制标记。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationRestartFlags&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 没有重启限制。如果仅指定 &amp;lt;see cref=&quot;None&quot;/&amp;gt;，那么操作系统在可以重启应用程序的时候都会重启应用。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 指定此时不重启：因未处理的异常而导致进程停止工作。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RestartNoCrash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 指定此时不重启：因应用程序无响应而导致进程停止工作。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RestartNoHang&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 指定此时不重启：因应用安装更新导致进程关闭。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RestartNoPatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 指定此时不重启：因操作系统安装更新后重启导致进程关闭。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RestartNoReboot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/67645160/answer/266972518&quot;&gt;为何win10 1709（秋季创意更新） 重启会自动恢复一些程序为重启以前的工作状态？ - 蒋晟的回答 - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/aa373347&quot;&gt;RegisterApplicationRestart function (Windows)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.pinvoke.net/default.aspx/kernel32.RegisterApplicationRestart&quot;&gt;pinvoke.net: RegisterApplicationRestart (kernel32)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/772868/Restart-Manager-Support-For-Windows-Application&quot;&gt;Restart Manager Support For Windows Application - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/32520036/6233938&quot;&gt;c# - Restart a crashed program with RegisterApplicationRestart without user prompt - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/17024/Vista-Application-Crash-Recovery-in-C&quot;&gt;Vista Application Crash Recovery in C# - CodeProject&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/32520036/6233938&quot;&gt;c# - Restart a crashed program with RegisterApplicationRestart without user prompt - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:13:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/application-restart-manager.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/application-restart-manager.html</guid>
        
        
        <category>csharp</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>为带有多种语言的 Jekyll 博客添加多语言选择</title>
        <description>&lt;p&gt;我有几篇博客是用多种语言编写的，一开始我是在每篇博客中添加其他语言的链接，但多语言博客多了之后就成了复制粘贴了。是时候做一个通用的布局来实现多语言博客了！&lt;/p&gt;

&lt;p&gt;本文将为大家提供一个我编写好的多语言博客选择器（MIT License）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;先来看看效果。现在，请选择一个阅读语言：&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;&quot;&gt;English&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;&quot;&gt;русский&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;&quot;&gt;繁體中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/multi-language-in-jekyll-blog.html&quot; selected=&quot;selected&quot;&gt;简体中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;&quot;&gt;日本語&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;&quot;&gt;ไทย&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;不要惊讶：其实这里的每一种语言都指向了你正在阅读的简体中文😜。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编写一个简单的语言选择器&quot;&gt;编写一个简单的语言选择器&lt;/h2&gt;

&lt;p&gt;html 里可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 来做选择器。当然，本文只是用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 当作例子，你也可以做成表格型的、链接型的或者其他更多更炫酷的样子。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 的最简例子（可以直接写到 markdown 里）：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;select&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post/multi-language-in-jekyll-blog.html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;English&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post/multi-language-in-jekyll-blog.html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;中文&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;来看看效果：
&lt;select style=&quot;{display:inline}&quot;&gt;
  &lt;option value=&quot;#&quot;&gt;English&lt;/option&gt;
  &lt;option value=&quot;#&quot;&gt;中文&lt;/option&gt;
&lt;/select&gt;&lt;/p&gt;

&lt;p&gt;然而，我们希望在点击的时候自动跳转到对应的链接。于是，我们为 &lt;code class=&quot;highlighter-rouge&quot;&gt;select&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;onchange&lt;/code&gt; 事件添加处理函数：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;select&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onchange=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;self.location.href=options[selectedIndex].value&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post/multi-language-in-jekyll-blog.html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;English&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post/multi-language-in-jekyll-blog.html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;中文&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;再试试选择一下：
&lt;select style=&quot;{display:inline}&quot; onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  &lt;option value=&quot;/post/multi-language-in-jekyll-blog.html&quot;&gt;English&lt;/option&gt;
  &lt;option value=&quot;/post/multi-language-in-jekyll-blog.html&quot;&gt;中文&lt;/option&gt;
&lt;/select&gt;&lt;/p&gt;

&lt;p&gt;这就可以生效了。&lt;/p&gt;

&lt;h2 id=&quot;引入页面配置元数据&quot;&gt;引入页面配置元数据&lt;/h2&gt;

&lt;p&gt;毕竟博客有多篇，终归要引入配置的。现在我们为这篇文章配置两种语言。&lt;em&gt;（考虑到更通用的情况，我将一种语言定义为一种 version。）&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;简体中文&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/post/multi-language-in-jekyll-blog.html&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;中文&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/post/multi-language-in-jekyll-blog.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个配置是要放到博客 markdown 的元数据头里的。&lt;/p&gt;

&lt;h2 id=&quot;制作布局文件&quot;&gt;制作布局文件&lt;/h2&gt;

&lt;p&gt;为了更加通用，我在 &lt;code class=&quot;highlighter-rouge&quot;&gt;_include&lt;/code&gt; 文件夹中新建了 &lt;code class=&quot;highlighter-rouge&quot;&gt;post-version-selector.html&lt;/code&gt; 的布局文件，然后在每一个需要引入语言选择器的地方加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;{% include post-version-selector.html %}&lt;/code&gt;。&lt;em&gt;（比如本文一开始的那个语言选择器就是通过在那个地方加上了这句话生成的。）&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;现在，我们把之前写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;select&lt;/code&gt; 搬到 &lt;code class=&quot;highlighter-rouge&quot;&gt;post-version-selector.html&lt;/code&gt; 文件中，并引入页面中配置好的各语言路径。&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{%- comment -%} MIT Licensed {%- endcomment -%}
{%- if page.versions -%}
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;select&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onchange=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;self.location.href=options[selectedIndex].value&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    {%- for version_hash in page.versions -%}
      {%- for version in version_hash -%}
        {%- assign key = version[0] -%}
        {%- assign value = version[1] -%}
        {%- if page.version.current == key -%}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.baseurl }}{{ page.url }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;selected=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;selected&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ key }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        {%- else -%}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ value }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ key }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        {%- endif -%}
      {%- endfor -%}
    {%- endfor -%}
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
{%- endif -%}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;统一解释一下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这里使用的 liquid 语言标记中都添加了短线 &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;{%- if condition -%}{%- endif -%}&lt;/code&gt;，这是为了将 liquid 语言占用的空行移除掉。
    &lt;ul&gt;
      &lt;li&gt;不同于原生的 html，在 markdown 中的 html 是受到空行影响的，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 的各个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt; 之间有空行，那么整个 &lt;code class=&quot;highlighter-rouge&quot;&gt;select&lt;/code&gt; 会被 &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown&lt;/code&gt; 解析器活生生拆掉。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;liquid 中如果要遍历 key-value 值，需要使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 来取出其中的 key 和 value。
    &lt;ul&gt;
      &lt;li&gt;就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;{%- for version in version_hash -%}&lt;/code&gt; 这一行，虽然有个 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt;，但其实只会执行一次。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/8206869/6233938&quot;&gt;jekyll - Iterate over hashes in liquid templates - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/3518002/6233938&quot;&gt;How can I set the default value for an HTML &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3cschool.cn/htmltags/tag-select.html&quot;&gt;超详细的HTML &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 标签用法及技巧介绍_w3cschool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://shopify.github.io/liquid/basics/whitespace/&quot;&gt;Whitespace control – Liquid template language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:13:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/multi-language-in-jekyll-blog.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/multi-language-in-jekyll-blog.html</guid>
        
        
        <category>site</category>
        
        <category>web</category>
        
        <category>html</category>
        
        <category>css</category>
        
      </item>
    
      <item>
        <title>屏幕上那个灰色带有数字的框是什么？看着好难受！</title>
        <description>&lt;p&gt;为什么屏幕上出现了一个灰框，里面有黑色数字，而且还不消失？强迫症难以忍受啊！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-07-51.png&quot; alt=&quot;各种背景下的灰框&quot; /&gt;&lt;br /&gt;
▲ 就是这个置于所有窗口最顶层，怎么也去不掉的灰色数字框&lt;/p&gt;

&lt;p&gt;强迫症晚期请直接前往&lt;a href=&quot;#%E8%A7%A3%E5%86%B3%E9%97%AE%E9%A2%98&quot;&gt;最后一节&lt;/a&gt;把它消灭好了，非强迫症晚期的我们一起来探究下它到底是什么。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用-spy&quot;&gt;使用 Spy++&lt;/h2&gt;

&lt;p&gt;想探究一个界面属于哪个进程，当然少不了 Spy++。现在，我们去 Visual Studio 中找到并打开 Spy++。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-10-02.png&quot; alt=&quot;启动 Spy++&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，我们会看到一个丑的不得了的 Spy++ 的界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-12-07.png&quot; alt=&quot;Spy++&quot; /&gt;&lt;/p&gt;

&lt;p&gt;紧接着，我们点击查找窗口（&lt;img src=&quot;/static/posts/2018-03-16-13-14-00.png&quot; alt=&quot;查找窗口&quot; /&gt; ）按钮开始查找窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-13-21.png&quot; alt=&quot;查找窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们发现，当我们将那个瞄准靶心指向灰色小窗口上时，这个窗口的句柄和其他信息已经显示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-17-47.png&quot; alt=&quot;查找窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是，点击“确定”来查看这个窗口的信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-23-37.png&quot; alt=&quot;居然是 Visual Studio 的某个子窗口&quot; /&gt;&lt;br /&gt;
▲ 居然是 Visual Studio 的某个名为 CandidateWindow 的子窗口&lt;/p&gt;

&lt;h2 id=&quot;猜测和搜索&quot;&gt;猜测和搜索&lt;/h2&gt;

&lt;p&gt;现在我们得到了这些线索：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;这是 Visual Studio 的窗口&lt;/li&gt;
  &lt;li&gt;这个窗口的类名叫做 &lt;code class=&quot;highlighter-rouge&quot;&gt;CandidateWindow&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;这一定是 Visual Studio 的 BUG，可以被我们疯狂吐槽&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在我们有了搜索关键字：Gray Box，Candidate Window，Visual Studio。&lt;/p&gt;

&lt;p&gt;搜索果然能发现有人遇到了这个问题（特别吐槽没有中文的，于是才有了本文）。有用的搜索资料见本文最后的 &lt;a href=&quot;#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99&quot;&gt;参考资料&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;解决问题&quot;&gt;解决问题&lt;/h2&gt;

&lt;p&gt;从搜索的结果中，我们可以得知，这是 Visual Studio 用来在 CodeLens 上显示辅助提示的指示窗口。解决方法便是&lt;strong&gt;在代码编辑窗口中长按 Alt 键重新打开辅助指示窗口，然后松开 Alt 键关掉这些窗口&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;试一试长按 Alt 键，果然出现了一模一样的窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-36-52.png&quot; alt=&quot;长按 Alt 打开的指示窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;松开 Alt 后，之前一直不消失的灰色数字窗口终于消失，世界顿时清静了。&lt;/p&gt;

&lt;h2 id=&quot;alt-指示窗口是什么&quot;&gt;Alt 指示窗口是什么？&lt;/h2&gt;

&lt;p&gt;其实这是 Windows 提供的一项功能，用于在仅有键盘的设备上能够操作各种菜单。下图是在资源管理器中长按 Alt 出来的键盘按键提示，按下键盘对应的键可以进入对应的功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-16-13-40-29.png&quot; alt=&quot;资源管理器的 Alt 指示&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/75736/grey-box-with-number-sticks-at-top-left-corner-of.html&quot;&gt;Grey box with number sticks at top left corner of screen - Developer Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/118174/number-in-upper-left-corner-of-screen.html&quot;&gt;number in upper-left corner of screen - Developer Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/content/problem/190178/visual-studio-leaving-numbers-in-tooltips-on-deskt.html&quot;&gt;Visual Studio leaving numbers in tooltips on desktop - Developer Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/27101609/6233938&quot;&gt;visual studio 2012 puts a small number in the top left corner of my screen - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:12:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/where-is-the-gray-candidate-window-come-from.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/where-is-the-gray-candidate-window-come-from.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>（C#）if (this == null)？你在逗我，this 怎么可能为 null！用 IL 编译和反编译看穿一切</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;if (this == null) Console.WriteLine(&quot;this is null&quot;);&lt;/code&gt; 这句话一写，大家一定觉得荒谬，然而 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 内代码的执行却是可能的！本文讲介绍到底发生了什么。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;制造一个-this-可以为-null-的程序&quot;&gt;制造一个 this 可以为 null 的程序&lt;/h2&gt;

&lt;p&gt;请看代码，这是我们的库函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;this is null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;this is not null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;外面是这样调用的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这代码写出来，当然毫不犹豫地说——这会发生 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;！&lt;/p&gt;

&lt;p&gt;然而……&lt;/p&gt;

&lt;p&gt;现在我们改一改 Program 的 IL：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-31-07-47-18.png&quot; alt=&quot;Foo.Test&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将关注重点放在图中红框标注的部分，那是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;p.Test&lt;/code&gt; 的地方。&lt;/p&gt;

&lt;p&gt;现在，我们将它从 &lt;code class=&quot;highlighter-rouge&quot;&gt;callvirt&lt;/code&gt; 修改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;第一步：反编译 exe 成 IL：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# ildasm 在 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.1 Tools\x64 路径下&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ildasm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D:\Desktop\wdemo.il&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Desktop\Walterlv.Demo\wdemo\bin\Debug\wdemo.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;第二步：修改 IL，将 callvirt 修改成 call&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;IL_0004:  call   instance void Walterlv.Demo.Foo::Test()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;第三步：重新编译 IL 成 exe&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# ilasm 在 C:\Windows\Microsoft.NET\Framework64\v4.0.30319 路径下&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ilasm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/out:D:\Desktop\wdemo.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;D:\Desktop\wdemo.il&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NET&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Assembler.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;4.7.2556.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copyright&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Corporation.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rights&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reserved.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assembling&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'D:\Desktop\wdemo.il'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EXE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'D:\Desktop\wdemo.exe'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ANSI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assembled&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.Program::Main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assembled&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.Program::.ctor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assembled&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.Foo::Test&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Assembled&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Walterlv.Demo.Foo::.ctor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Creating&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emitting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classes:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Walterlv&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Emitting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Resolving&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;defs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;unresolved&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Emitting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Global&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Resolving&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;defs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;unresolved&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Writing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;successfully&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;结果，现在再执行程序时，输出是 &lt;code class=&quot;highlighter-rouge&quot;&gt;this is null&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-31-08-10-52.png&quot; alt=&quot;this is null&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;为什么此时-this-是-null&quot;&gt;为什么此时 this 是 null&lt;/h2&gt;

&lt;p&gt;从名字上看，&lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt; 是为了调用非虚方法、静态方法或者基类方法的；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;callvirt&lt;/code&gt; 是为了调用虚方法的。前者在编译时就将确认调用了某个类的某个方法，而后者将在运行时动态决定应该调用哪个。&lt;/p&gt;

&lt;p&gt;然而，当 IL 试图调用某个变量实例的一个方法时，由于不确定这个变量到底是不是实际的类型（还是基类型），所以都采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;callvirt&lt;/code&gt; 进行调用。&lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt; 在编译时就已确定调用，所以也没有加入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的判断；&lt;code class=&quot;highlighter-rouge&quot;&gt;callvirt&lt;/code&gt; 却需要，因为通常都是实例使用。&lt;/p&gt;

&lt;p&gt;于是，此次便出现了 &lt;code class=&quot;highlighter-rouge&quot;&gt;null.Test()&lt;/code&gt; 这样诡异的调用。&lt;/p&gt;

&lt;h2 id=&quot;一些建议和总结&quot;&gt;一些建议和总结&lt;/h2&gt;

&lt;p&gt;虽然我们制造出了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 可能为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的情况，即便库和调用方是分开开发的，但实际开发中其实并不需要考虑这样的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/abhinaba/2007/07/26/easy-way-to-modify-il-code/&quot;&gt;Easy way to modify IL code – I know the answer (it’s 42)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/193952/6233938&quot;&gt;.net - Call and Callvirt - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.paranoidcoding.com/2015/03/11/observing-a-null-this.html&quot;&gt;Observing a null this value&lt;/a&gt;
&lt;!-- - [用CIL写程序:从“call vs callvirt”看方法调用 - 陈嘉栋 - 博客园](http://www.cnblogs.com/murongxiaopifu/p/4298167.html) --&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:12:18 +0000</pubDate>
        <link>https://blog.walterlv.com/post/this-could-be-null.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/this-could-be-null.html</guid>
        
        <category>ilasm</category>
        
        <category>ildasm</category>
        
        
        <category>dotnet</category>
        
        <category>msil</category>
        
      </item>
    
      <item>
        <title>使用 GitVersion 在编译或持续构建时自动使用语义版本号（Semantic Versioning）</title>
        <description>&lt;p&gt;我们在之前谈过 &lt;a href=&quot;/post/semantic-version&quot;&gt;语义版本号（Semantic Versioning）&lt;/a&gt;，在项目中应用语义版本号能够帮助库的开发者在发布包时表明更多的语义信息。这是趋势，从微软的博客 &lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/03/versioning-nuget-packages-cd-1/&quot;&gt;Versioning NuGet packages in a continuous delivery world&lt;/a&gt; 三部曲中可以看出，从 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本开始支持语义版本号 2.0 也能看出。&lt;/p&gt;

&lt;p&gt;本文将从持续集成的角度来说语义版本号，告诉大家如何自动生成包含语义的版本号，并在发布库时采用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/automatically-semantic-versioning-using-git-version-task.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/automatically-semantic-versioning-using-git-version-task.en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;安装-gitversiontask&quot;&gt;安装 GitVersionTask&lt;/h2&gt;

&lt;p&gt;微软工程师在博客 &lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/26/versioning-nuget-packages-cd-3/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog&lt;/a&gt; 中推荐的语义版本号生成工具是 GitVersion。从实际寻找来看，这似乎也是唯一一个能够让 NuGet 包支持语义版本号的工具。&lt;/p&gt;

&lt;p&gt;去 &lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet.org&lt;/a&gt; 上为我们的库项目安装 &lt;a href=&quot;https://www.nuget.org/packages/GitVersionTask&quot;&gt;GitVersionTask&lt;/a&gt; 即可开始我们的语义版本号。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;请特别注意&lt;/strong&gt;：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;目前只有 &lt;a href=&quot;https://www.nuget.org/packages/GitVersionTask/4.0.0-beta0012&quot;&gt;GitVersionTask &lt;strong&gt;4.0 以上&lt;/strong&gt;的版本&lt;/a&gt;（目前都是 beta）才支持 .NET Core 那样新格式的 csproj。&lt;/li&gt;
  &lt;li&gt;目前即便是最新测试版的 GitVersionTask 也&lt;strong&gt;不支持使用基于 .NET Core 的&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 编译，原因和解决方案我已经提交给 GitTools 团队了（详见：&lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1399&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion&lt;/a&gt;），临时方案是使用 .NET Framework 版本的 &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;配置-gitversion&quot;&gt;配置 GitVersion&lt;/h2&gt;

&lt;p&gt;特别吐槽一下 GitVersion 的官方文档，把功能堆积得很多很强大，却忽视了面向新手的入门教程。&lt;/p&gt;

&lt;p&gt;GitVersion 的配置文件名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;GitVersion.yml&lt;/code&gt;，要求放到仓库的根目录下。官方文档对于配置文件的解释非常抽象，看完也不知道值应该写成什么样，也不知道每个值代表什么意义。于是我基本上是通过阅读它的源码来了解配置文件的实际含义的。&lt;/p&gt;

&lt;p&gt;经过一番折腾，我把配置文件改成了下面这样。&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;next-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Inherit&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;tag-prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[vV]'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;source-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;master'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;develop'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hotfix'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;sha&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commits-before&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2018-01-01T00:00:00&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master$&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;r(elease$|(eleases)?[-/])&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;beta&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;feature&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;f(eatures)?[-/]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDeployment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;alpha&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Minor&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ 别看这配置文件写得这么长，但其实官方的默认配置文件更长！&lt;/p&gt;

&lt;p&gt;好了不开玩笑了，这配置文件分两部分来看：1. &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之前；2. &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之后。&lt;/p&gt;

&lt;p&gt;写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之前的为全局配置，写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之后的是按分支分类的配置；它们的配置键值其实都是一样的。分支里的配置优先级高于全局配置。也就是说，如果编译打包的分支名能被 &lt;code class=&quot;highlighter-rouge&quot;&gt;regex&lt;/code&gt; 正则表达式匹配上，那么就使用匹配的分支配置，否则使用全局配置。&lt;/p&gt;

&lt;p&gt;举例，假设我们现在的版本库是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-15-34-08.png&quot; alt=&quot;版本库&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;分支名称匹配-regex&quot;&gt;分支名称匹配 &lt;code class=&quot;highlighter-rouge&quot;&gt;regex&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;那么当我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的 &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; 提交上编译，使用的配置将是 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的配置。&lt;/p&gt;

&lt;p&gt;由于我将 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的正则表达式写成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;r(elease$|(eleases)?[-/])&lt;/code&gt;（注意，我们不需要加行首标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;^&lt;/code&gt;，因为 GitVersionTask 里会为我们在最前面加一个），所以类似这样的分支名也是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的配置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;r/1.2.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;releases/1.2.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，这样的分支名将采用默认的全局配置（因为不符合正则表达式）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;r&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;releases&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上配置中我只列举了三组分支，但其实在 &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;一个成功的 Git 分支流模型&lt;/a&gt; 中，还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;hotfix&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 这样更多的分支。如果你的项目足够大，建议自己参考其他分支写出这两个分支的配置出来。&lt;/p&gt;

&lt;h3 id=&quot;预发布标签-tag&quot;&gt;预发布标签 &lt;code class=&quot;highlighter-rouge&quot;&gt;tag&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;我们的 release 配置中，会为版本号加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 预发布标签，所以可能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-beta&lt;/code&gt; 这样的包出来，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-beta+3&lt;/code&gt;。但在全局配置下，默认打出的包会加一个以分支名命名的预发布标签；像这样 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-r&lt;/code&gt;（在 &lt;code class=&quot;highlighter-rouge&quot;&gt;r&lt;/code&gt; 分支），或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-temp-walterlv-custombranch&lt;/code&gt;（在 &lt;code class=&quot;highlighter-rouge&quot;&gt;temp/walterlv/custombranch&lt;/code&gt; 分支）。&lt;/p&gt;

&lt;p&gt;继续看以上的配置，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;f/blog&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;features/new&lt;/code&gt; 分支上将采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha&lt;/code&gt; 预发布标签。&lt;/p&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支的配置上&lt;/p&gt;

&lt;h3 id=&quot;版本号递增规则-increment&quot;&gt;版本号递增规则 &lt;code class=&quot;highlighter-rouge&quot;&gt;increment&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;increment&lt;/code&gt; 这一项的可选值有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Major&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Minor&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Patch&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inherit&lt;/code&gt; 五种。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Major&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 2.0.0 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Minor&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.3.0 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Patch&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.2.1 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.2.0 的包来&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Inherit&lt;/code&gt; 如果此分支上没有发现能够确认版本号的线索（例如一个 Tag），那么将自动寻找此分支的来源分支，继承来源分支的版本号递增规则。注意我在全局配置中加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;source-branches&lt;/code&gt; 配置，用于指定如果要自动寻找来源分支，请去这个集合中指定的分支名称里找。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下图是 release 分支上打包的版本号。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-16-27-11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;版本号递增的方式-mode&quot;&gt;版本号递增的方式 &lt;code class=&quot;highlighter-rouge&quot;&gt;mode&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mode&lt;/code&gt; 可选的值有三种：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;continuous-delivery&lt;/code&gt; 持续交付，临近产品发布时使用，详细信息可阅读&lt;a href=&quot;http://gitversion.readthedocs.io/en/stable/reference/continuous-delivery/&quot;&gt;Continous delivery - GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;continuous-deployment&lt;/code&gt; 持续部署，日常使用，详细信息可阅读&lt;a href=&quot;http://gitversion.readthedocs.io/en/stable/reference/continuous-deployment/&quot;&gt;Continuous deployment - GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Mainline&lt;/code&gt; 传统的（官方文档没有说明，代码中没有注释，但阅读代码发现其策略是从上一个 Tag 递增版本号）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;语义版本号使用教程&quot;&gt;语义版本号使用教程&lt;/h2&gt;

&lt;p&gt;在了解了以上的配置之后，使用 GitVersionTask 才不会显得版本号的规则诡异。&lt;/p&gt;

&lt;p&gt;我们从简单的使用开始，逐步向难演进。学习规则为：单个 master 分支 -&amp;gt; Git 分支流与预发布版本&lt;/p&gt;

&lt;h3 id=&quot;单个-master-分支&quot;&gt;单个 master 分支&lt;/h3&gt;

&lt;p&gt;如果我们只在 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 上开发，那么上手就非常容易了。&lt;/p&gt;

&lt;p&gt;如果我们刚开始接触 GitVersionTask，那么我们在上一个发布包的提交上新建一个标签（Tag），命名为 v1.2.0，那么此标签之后的版本号打包将自动变为 1.2.1。Git 提交每次增多，那么构建号将加 1。下图中的版本号是 1.2.1+3。（注意：加号是语义版本号 2.0 的新特性，重申需要 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-17-15-09.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;git-分支流与预发布版本&quot;&gt;Git 分支流与预发布版本&lt;/h3&gt;

&lt;p&gt;当使用 Git 分支流时，版本号的递增方式其实与前面配置章节和单个 master 章节讲的时一致的。如下图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-17-25-20.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，我们需要学习如何充分利用这样的分支流，以便让语义版本号充分发挥它的作用。&lt;/p&gt;

&lt;p&gt;假设：&lt;em&gt;我们最近发布了 1.1.0 正式版。&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果我们正在为库添加新功能，则新建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支，一直开发，直到认为开发完毕（功能实现完成，单元测试全绿）&lt;/li&gt;
  &lt;li&gt;如果此时有打包需求临时内测，则直接在 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支打包，这样能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0-alpha&lt;/code&gt; 的包（后面的 + 取决于相对于此前发布多了多少个提交）&lt;/li&gt;
  &lt;li&gt;如果内测差不多了，则合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支确认这个内侧包&lt;/li&gt;
  &lt;li&gt;如果准备发布这个功能了，那么从 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分到 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支&lt;/li&gt;
  &lt;li&gt;这时如果有打包需求，则应该在打包之前&lt;strong&gt;新建一个标签&lt;/strong&gt;（Tag）&lt;code class=&quot;highlighter-rouge&quot;&gt;v1.2-beta&lt;/code&gt;，这样能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 包（而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.1&lt;/code&gt; 的）&lt;/li&gt;
  &lt;li&gt;如果在此 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 的基础上出现持续打包，那么需要持续新建标签（因为自动新建的标签只会增加一次 Patch 号）&lt;/li&gt;
  &lt;li&gt;如果确认可正式发布，则 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt;，新建 &lt;code class=&quot;highlighter-rouge&quot;&gt;v1.2&lt;/code&gt; 标签&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/03/versioning-nuget-packages-cd-1/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 1 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/18/versioning-nuget-packages-cd-2/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 2 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/26/versioning-nuget-packages-cd-3/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/49765756/6233938&quot;&gt;C#/.NET - How to generate and increase package version automatically especially via CI? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion&quot;&gt;GitTools/GitVersion: Easy Semantic Versioning (http://semver.org) for projects using Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gitversion.readthedocs.io/en/latest/&quot;&gt;GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1349&quot;&gt;Gitversion Task for VS2017-style csproj · Issue #1349 · GitTools/GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins.io/display/JENKINS/Change+Assembly+Version&quot;&gt;Change Assembly Version - Jenkins - Jenkins Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeffkl/RoslynCodeTaskFactory/issues/15&quot;&gt;Not working in .NET Core v2.0 project · Issue #15 · jeffkl/RoslynCodeTaskFactory&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/RoslynCodeTaskFactory/1.2.1&quot;&gt;NuGet Gallery - RoslynCodeTaskFactory 1.2.1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1399&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/msbuild/issues/2111&quot;&gt;.NET Core MSBuild cannot load tasks built against MSBuild 4.0 · Issue #2111 · Microsoft/msbuild&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/sdk/issues/1870&quot;&gt;Should the SDK include Microsoft.Build.Utilities.v4.0? · Issue #1870 · dotnet/sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:11:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/automatically-semantic-versioning-using-git-version-task.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/automatically-semantic-versioning-using-git-version-task.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Automatically increase the semantic version using GitVersion</title>
        <description>&lt;p&gt;I wrote another post talking about &lt;a href=&quot;/post/semantic-version&quot;&gt;Semantic Versioning&lt;/a&gt; before (&lt;em&gt;but it is not in English&lt;/em&gt;). Introducing the semantic version to a project can give library users more semantic information when library developers publishing packages. From the Microsoft blog &lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/03/versioning-nuget-packages-cd-1/&quot;&gt;Versioning NuGet packages in a continuous delivery world&lt;/a&gt; we could find that semantic versioning is the trend.&lt;/p&gt;

&lt;p&gt;This article will refer to the semantic versioning from the perspective of continuous integration, telling you how to automatically generate a version that contains semantic, and use it when publishing the library.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/automatically-semantic-versioning-using-git-version-task.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/automatically-semantic-versioning-using-git-version-task.en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;install-the-gitversiontask&quot;&gt;Install the GitVersionTask&lt;/h2&gt;

&lt;p&gt;A Microsoft engineer recommend a semantic versioning tools named &lt;code class=&quot;highlighter-rouge&quot;&gt;GitVersion&lt;/code&gt; on his blog &lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/26/versioning-nuget-packages-cd-3/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog&lt;/a&gt;. I tried to find more tools but unfortunately the GitVersion seems to be the only one that can add semantic version to a nuget package.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet.org&lt;/a&gt; to install &lt;a href=&quot;https://www.nuget.org/packages/GitVersionTask&quot;&gt;GitVersionTask&lt;/a&gt; for our library project and then we will start our semantic versioning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Only &lt;a href=&quot;https://www.nuget.org/packages/GitVersionTask/4.0.0-beta0012&quot;&gt;GitVersionTask &lt;strong&gt;4.0 or later&lt;/strong&gt;&lt;/a&gt; (currently beta) supports new csproj format which is introduced from .NET Core.&lt;/li&gt;
  &lt;li&gt;Currently even the latest beta version of GitVersionTask does not support the .NET Core-based compilation - &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt;. I’ve submitted an issue to the GitTools team to explain the reason and the solution. (see: &lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1399&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion&lt;/a&gt;) The temporary fallback is to use a full .NET Framework version - &lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;gitversion-configuration&quot;&gt;GitVersion Configuration&lt;/h2&gt;

&lt;p&gt;GitVersion official documentation is not easy to read. I cannot find even detailed meaning of each configuration keys and values. But I read it’s source code, and these are the meanings below.&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;next-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Inherit&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;tag-prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[vV]'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;source-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;master'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;develop'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hotfix'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;sha&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commits-before&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2018-01-01T00:00:00&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master$&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;r(elease$|(eleases)?[-/])&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDelivery&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;beta&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;feature&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;f(eatures)?[-/]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ContinuousDeployment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;alpha&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Minor&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prevent-increment-of-merged-branch-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;track-merge-target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracks-release-branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;is-release-branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ It’s long, but the official one is longer.&lt;/p&gt;

&lt;p&gt;// TODO: &lt;strong&gt;Translation is interrupted and I’ll translate below later.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;好了不开玩笑了，这配置文件分两部分来看：1. &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之前；2. &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之后。&lt;/p&gt;

&lt;p&gt;写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之前的为全局配置，写在 &lt;code class=&quot;highlighter-rouge&quot;&gt;branches&lt;/code&gt; 之后的是按分支分类的配置；它们的配置键值其实都是一样的。分支里的配置优先级高于全局配置。也就是说，如果编译打包的分支名能被 &lt;code class=&quot;highlighter-rouge&quot;&gt;regex&lt;/code&gt; 正则表达式匹配上，那么就使用匹配的分支配置，否则使用全局配置。&lt;/p&gt;

&lt;p&gt;举例，假设我们现在的版本库是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-15-34-08.png&quot; alt=&quot;版本库&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;分支名称匹配-regex&quot;&gt;分支名称匹配 &lt;code class=&quot;highlighter-rouge&quot;&gt;regex&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;那么当我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的 &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; 提交上编译，使用的配置将是 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的配置。&lt;/p&gt;

&lt;p&gt;由于我将 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的正则表达式写成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;r(elease$|(eleases)?[-/])&lt;/code&gt;（注意，我们不需要加行首标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;^&lt;/code&gt;，因为 GitVersionTask 里会为我们在最前面加一个），所以类似这样的分支名也是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支的配置：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;r/1.2.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;releases/1.2.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是，这样的分支名将采用默认的全局配置（因为不符合正则表达式）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;r&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;releases&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上配置中我只列举了三组分支，但其实在 &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;一个成功的 Git 分支流模型&lt;/a&gt; 中，还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;hotfix&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 这样更多的分支。如果你的项目足够大，建议自己参考其他分支写出这两个分支的配置出来。&lt;/p&gt;

&lt;h3 id=&quot;预发布标签-tag&quot;&gt;预发布标签 &lt;code class=&quot;highlighter-rouge&quot;&gt;tag&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;我们的 release 配置中，会为版本号加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 预发布标签，所以可能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-beta&lt;/code&gt; 这样的包出来，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-beta+3&lt;/code&gt;。但在全局配置下，默认打出的包会加一个以分支名命名的预发布标签；像这样 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-r&lt;/code&gt;（在 &lt;code class=&quot;highlighter-rouge&quot;&gt;r&lt;/code&gt; 分支），或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;2.0.0-temp-walterlv-custombranch&lt;/code&gt;（在 &lt;code class=&quot;highlighter-rouge&quot;&gt;temp/walterlv/custombranch&lt;/code&gt; 分支）。&lt;/p&gt;

&lt;p&gt;继续看以上的配置，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;f/blog&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;features/new&lt;/code&gt; 分支上将采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha&lt;/code&gt; 预发布标签。&lt;/p&gt;

&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 分支的配置上&lt;/p&gt;

&lt;h3 id=&quot;版本号递增规则-increment&quot;&gt;版本号递增规则 &lt;code class=&quot;highlighter-rouge&quot;&gt;increment&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;increment&lt;/code&gt; 这一项的可选值有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Major&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Minor&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Patch&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inherit&lt;/code&gt; 五种。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Major&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 2.0.0 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Minor&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.3.0 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Patch&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.2.1 的包来（无论此分支当前距离那个 Tag 有多少个提交，都只加 1）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag，那么现在将打出 1.2.0 的包来&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Inherit&lt;/code&gt; 如果此分支上没有发现能够确认版本号的线索（例如一个 Tag），那么将自动寻找此分支的来源分支，继承来源分支的版本号递增规则。注意我在全局配置中加了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;source-branches&lt;/code&gt; 配置，用于指定如果要自动寻找来源分支，请去这个集合中指定的分支名称里找。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下图是 release 分支上打包的版本号。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-16-27-11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;版本号递增的方式-mode&quot;&gt;版本号递增的方式 &lt;code class=&quot;highlighter-rouge&quot;&gt;mode&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mode&lt;/code&gt; 可选的值有三种：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;continuous-delivery&lt;/code&gt; 持续交付，临近产品发布时使用，详细信息可阅读&lt;a href=&quot;http://gitversion.readthedocs.io/en/stable/reference/continuous-delivery/&quot;&gt;Continous delivery - GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;continuous-deployment&lt;/code&gt; 持续部署，日常使用，详细信息可阅读&lt;a href=&quot;http://gitversion.readthedocs.io/en/stable/reference/continuous-deployment/&quot;&gt;Continuous deployment - GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Mainline&lt;/code&gt; 传统的（官方文档没有说明，代码中没有注释，但阅读代码发现其策略是从上一个 Tag 递增版本号）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;语义版本号使用教程&quot;&gt;语义版本号使用教程&lt;/h2&gt;

&lt;p&gt;在了解了以上的配置之后，使用 GitVersionTask 才不会显得版本号的规则诡异。&lt;/p&gt;

&lt;p&gt;我们从简单的使用开始，逐步向难演进。学习规则为：单个 master 分支 -&amp;gt; Git 分支流与预发布版本&lt;/p&gt;

&lt;h3 id=&quot;单个-master-分支&quot;&gt;单个 master 分支&lt;/h3&gt;

&lt;p&gt;如果我们只在 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; 上开发，那么上手就非常容易了。&lt;/p&gt;

&lt;p&gt;如果我们刚开始接触 GitVersionTask，那么我们在上一个发布包的提交上新建一个标签（Tag），命名为 v1.2.0，那么此标签之后的版本号打包将自动变为 1.2.1。Git 提交每次增多，那么构建号将加 1。下图中的版本号是 1.2.1+3。（注意：加号是语义版本号 2.0 的新特性，重申需要 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-17-15-09.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;git-分支流与预发布版本&quot;&gt;Git 分支流与预发布版本&lt;/h3&gt;

&lt;p&gt;当使用 Git 分支流时，版本号的递增方式其实与前面配置章节和单个 master 章节讲的时一致的。如下图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-13-17-25-20.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，我们需要学习如何充分利用这样的分支流，以便让语义版本号充分发挥它的作用。&lt;/p&gt;

&lt;p&gt;假设：&lt;em&gt;我们最近发布了 1.1.0 正式版。&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果我们正在为库添加新功能，则新建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支，一直开发，直到认为开发完毕（功能实现完成，单元测试全绿）&lt;/li&gt;
  &lt;li&gt;如果此时有打包需求临时内测，则直接在 &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt; 分支打包，这样能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0-alpha&lt;/code&gt; 的包（后面的 + 取决于相对于此前发布多了多少个提交）&lt;/li&gt;
  &lt;li&gt;如果内测差不多了，则合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支确认这个内侧包&lt;/li&gt;
  &lt;li&gt;如果准备发布这个功能了，那么从 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分到 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 分支&lt;/li&gt;
  &lt;li&gt;这时如果有打包需求，则应该在打包之前&lt;strong&gt;新建一个标签&lt;/strong&gt;（Tag）&lt;code class=&quot;highlighter-rouge&quot;&gt;v1.2-beta&lt;/code&gt;，这样能打出 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 包（而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;1.1&lt;/code&gt; 的）&lt;/li&gt;
  &lt;li&gt;如果在此 &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; 的基础上出现持续打包，那么需要持续新建标签（因为自动新建的标签只会增加一次 Patch 号）&lt;/li&gt;
  &lt;li&gt;如果确认可正式发布，则 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt;，新建 &lt;code class=&quot;highlighter-rouge&quot;&gt;v1.2&lt;/code&gt; 标签&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/03/versioning-nuget-packages-cd-1/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 1 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/18/versioning-nuget-packages-cd-2/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 2 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/devops/2016/05/26/versioning-nuget-packages-cd-3/&quot;&gt;Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/49765756/6233938&quot;&gt;C#/.NET - How to generate and increase package version automatically especially via CI? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion&quot;&gt;GitTools/GitVersion: Easy Semantic Versioning (http://semver.org) for projects using Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gitversion.readthedocs.io/en/latest/&quot;&gt;GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1349&quot;&gt;Gitversion Task for VS2017-style csproj · Issue #1349 · GitTools/GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins.io/display/JENKINS/Change+Assembly+Version&quot;&gt;Change Assembly Version - Jenkins - Jenkins Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeffkl/RoslynCodeTaskFactory/issues/15&quot;&gt;Not working in .NET Core v2.0 project · Issue #15 · jeffkl/RoslynCodeTaskFactory&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/RoslynCodeTaskFactory/1.2.1&quot;&gt;NuGet Gallery - RoslynCodeTaskFactory 1.2.1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/GitTools/GitVersion/issues/1399&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/msbuild/issues/2111&quot;&gt;.NET Core MSBuild cannot load tasks built against MSBuild 4.0 · Issue #2111 · Microsoft/msbuild&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/sdk/issues/1870&quot;&gt;Should the SDK include Microsoft.Build.Utilities.v4.0? · Issue #1870 · dotnet/sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:11:36 +0000</pubDate>
        <link>https://blog.walterlv.com/post/automatically-semantic-versioning-using-git-version-task.en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/automatically-semantic-versioning-using-git-version-task.en.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>nuget</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使用 Emit 生成 IL 代码</title>
        <description>&lt;p&gt;.NET Core/.NET Framework 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Reflection.Emit&lt;/code&gt; 命名空间为我们提供了动态生成 IL 代码的能力。利用这项能力，我们能够在运行时生成一段代码/一个方法/一个类/一个程序集。&lt;/p&gt;

&lt;p&gt;本文将介绍使用 Emit 生成 IL 代码的方法，以及在此过程中可能遇到的各种问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在编写以下代码时如果遇到一些意料之外的错误，希望调试生成的 IL 代码，可以尝试阅读 &lt;a href=&quot;/post/how-to-quickly-write-emit-code&quot;&gt;如何快速编写和调试 Emit 生成 IL 的代码&lt;/a&gt; 了解如何调试和解决。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;用 Emit 生成 IL 代码时，很多我们写 C# 时不会注意到的问题现在都需要开始留意。&lt;/p&gt;

&lt;p&gt;在阅读本文之前，希望统一一个平时可能不太留意的英文：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;形参：parameter&lt;/li&gt;
  &lt;li&gt;实参：argument&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果不了解它们之间的区别，请自行搜索。&lt;/p&gt;

&lt;h2 id=&quot;定义方法签名&quot;&gt;定义方法签名&lt;/h2&gt;

&lt;p&gt;在 IL 中，方法名称可以使用比 C# 更多的字符，例如“&amp;lt;”和“&amp;gt;”，这也是 C# 编译闭包时喜欢使用的字符。目前我还没有找到 IL 中哪些字符可以作为标识符名称，但从混淆工具来看，是比 C# 多得多的。&lt;/p&gt;

&lt;p&gt;如果你试图生成实例方法，那么实例本身 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 将成为第一个参数，不过并不需要额外将它定义到参数列表中。&lt;/p&gt;

&lt;p&gt;当然，如果是静态方法，我们能够自己指定一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 参数，不过没有实际的意义。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DynamicMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;MethodName&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;this&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParameterAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果不声明形参，那么生成的 IL 代码的函数将无法被正常调用（提示可能造成运行时的不稳定）。&lt;/p&gt;

&lt;h2 id=&quot;声明和初始化局部变量&quot;&gt;声明和初始化局部变量&lt;/h2&gt;

&lt;p&gt;平时写 C# 的时候，可能一个方法里面没有定义任何一个局部变量，但 IL 可不一定这么认为。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上，在 IL 中，除了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 之外，还会额外定义一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool&lt;/code&gt; 类型的局部变量 &lt;code class=&quot;highlighter-rouge&quot;&gt;V_1&lt;/code&gt;。在 &lt;code class=&quot;highlighter-rouge&quot;&gt;value.GetType() == typeof(string)&lt;/code&gt; 执行完后，其值将存入 &lt;code class=&quot;highlighter-rouge&quot;&gt;V_1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以，如果需要编写 Emit 生成代码的代码，需要注意这些隐式产生的局部变量，它们需要和普通变量一样被初始化。&lt;/p&gt;

&lt;p&gt;Emit 代码为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 这就声明了两个局部变量。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeclareLocal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeclareLocal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;定义标签&quot;&gt;定义标签&lt;/h2&gt;

&lt;p&gt;如果代码中存在非线性结构，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt;，那么 IL 就需要知道跳转的地址。那么如何能够知道跳转到哪个地址呢？&lt;/p&gt;

&lt;p&gt;——&lt;strong&gt;使用标签&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;对 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 来说，&lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 操作需要知道 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 的起始地址；对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 内部的结尾来说，需要知道整个 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 结束之后的第一个操作的地址。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startOfElse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endOfWholeIfElse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefineLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其他生成代码。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 如果 if 条件不满足，跳转到 startOfElse。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Brfalse_S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startOfElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其他生成代码。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 在 if 结束之后，跳转到 endOfWholeIfElse 地址。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Br_S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endOfWholeIfElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其他生成代码。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 假设这里到了 else 的开头了，于是将 startOfElse 进行标记。标记完紧跟着写 else 部分的代码。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MarkLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startOfElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其他生成代码。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 假设这里整个 if-else 结束了，于是将 endOfWholeIfElse 进行标记。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;il&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MarkLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endOfWholeIfElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;生成方法签名与元数据
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.parameterbuilder(v=vs.110).aspx&quot;&gt;ParameterBuilder Class (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.defineparameter(v=vs.110).aspx&quot;&gt;MethodBuilder.DefineParameter Method (Int32, ParameterAttributes, String) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/9zksbcwc(v=vs.100).aspx&quot;&gt;Defining a Parameter with Reflection Emit&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/33656409/6233938&quot;&gt;c# - How to set “.maxstack” with ILGenerator - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;生成方法体
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.definelabel(v=vs.110).aspx&quot;&gt;ILGenerator.DefineLabel Method (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.marklabel(v=vs.110).aspx&quot;&gt;ILGenerator.MarkLabel Method (Label) (System.Reflection.Emit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/15279066/6233938&quot;&gt;c# - Emit local variable and assign a value to it - Stack Overflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/11139241/6233938&quot;&gt;C# reflection: If … else? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:11:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/generate-il-using-emit.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/generate-il-using-emit.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间（xmlns）和命名空间前缀</title>
        <description>&lt;p&gt;在 WPF XAML 中使用自己定义的控件时，想必大家都能在 XAML 中编写出这个控件的命名空间了。&lt;strong&gt;然而——我写不出来，除非借助 ReSharper。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果控件能够有一个漂亮的命名空间和命名空间前缀呢？——好吧，还是写不出来，不过，至少漂亮些。本文将指导你自定义在 XAML 中使用的命名空间。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;达到什么样的效果&quot;&gt;达到什么样的效果？&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HuyaHearhira.UserControl1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:w=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://walterlv.github.io/demo&quot;&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;&amp;lt;Grid&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;w:DemoPage&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;DemoPage&lt;/code&gt; 所在的命名空间了吗？是 &lt;code class=&quot;highlighter-rouge&quot;&gt;http://walterlv.github.io/demo&lt;/code&gt; 哦。而且，命名空间前缀是 &lt;code class=&quot;highlighter-rouge&quot;&gt;w&lt;/code&gt;。这是不是比下面这种看得清爽多了呢？&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HuyaHearhira.UserControl1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:newCsprojDemo=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.NewCsprojDemo;assembly=Walterlv.NewCsprojDemo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;newCsprojDemo:DemoPage&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，好处不止是更清爽，还有更多，总结起来是这三个：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;利于 API 的升级&lt;br /&gt;
 例如你写了一个库提供了一些可以在 XAML 中使用的控件，但是后来随着功能的强大你把程序集拆分成了多个。这时，如果没有这样的命名空间定义，那就意味着使用你的库的大量开发者需要手工修改 XAML 中的命名空间前缀定义。而使用了这样的命名空间定义的方法后，开发者只需要重新编译一遍即可。&lt;/li&gt;
  &lt;li&gt;简化命名空间前缀&lt;br /&gt;
 如果你的库有多个命名空间下都提供控件，那么可以使用命名空间定义将这些 C#/.NET 命名空间都映射到同一个 url 下，使得 XAML 中的命名空间声明可以更少。&lt;/li&gt;
  &lt;li&gt;更加清晰的命名空间声明 
 可以通过将命名空间前缀定义得更加清晰，更有效地利用每一个字符，而不是一些结构化的 &lt;code class=&quot;highlighter-rouge&quot;&gt;clr-namespace&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;assembly&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;这是怎么做到的呢&quot;&gt;这是怎么做到的呢？&lt;/h2&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Markup&lt;/code&gt; 命名空间下，有两个程序集级别的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt;，分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlnsDefinition&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;XmlnsPrefix&lt;/code&gt;。&lt;code class=&quot;highlighter-rouge&quot;&gt;XmlnsDefinition&lt;/code&gt; 定义某个 C# 命名空间和一段命名空间字符串是等意的，&lt;code class=&quot;highlighter-rouge&quot;&gt;XmlnsPrefix&lt;/code&gt; 定义此命名空间的默认前缀（只是默认而已）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://walterlv.github.io/demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NewCsprojDemo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://walterlv.github.io/demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，利用这两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 能够达到本文一开始的奇妙的效果。&lt;/p&gt;

&lt;p&gt;如果你用工具（例如 ReSharper）自动生成命名空间前缀时，才会使用这样默认的命名空间前缀，否则，你随便填。&lt;/p&gt;

&lt;h2 id=&quot;还有什么更高级的玩法吗&quot;&gt;还有什么更高级的玩法吗？&lt;/h2&gt;

&lt;p&gt;也许你注意到 WPF 有一些一开始就帮你生成好的命名空间前缀，例如这些：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果我们也把命名空间定义到这里会如何呢？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XmlnsDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Walterlv.NewCsprojDemo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;哇，我们竟然可以不用带前缀啦！&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;UserControl&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HuyaHearhira.UserControl1&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DemoPage&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserControl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这在项目内为一些几乎侵染全部代码的标记扩展是很棒的一波语法糖。例如——自己实现的本地化标记扩展。&lt;/p&gt;

&lt;h2 id=&quot;一些限制&quot;&gt;一些限制&lt;/h2&gt;

&lt;p&gt;值得注意的是，XAML 命名空间的定义只会在外部程序集生效。这是说，如果你在 A 程序集中定义了命名空间，那么只有引用了 A 程序集的 B 或者 C 才可以使用到新定义的命名空间；A 程序集自身是没有办法使用此命名空间的。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2778489/6233938&quot;&gt;wpf - How to make XmlnsDefinition work on the local assembly? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/7e7a032a-dad3-4e02-9e5a-d73e346b75ed/xmlnsdefinition-doesnt-work-in-the-same-assembly&quot;&gt;XmlnsDefinition doesn’t work in the same assembly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:04:53 +0000</pubDate>
        <link>https://blog.walterlv.com/post/define-xmlns-of-for-xaml.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/define-xmlns-of-for-xaml.html</guid>
        
        
        <category>dotnet</category>
        
        <category>xaml</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>git subtree 不断增加的推送时间，解不玩的冲突！这篇文章应该能救你</title>
        <description>&lt;p&gt;原生 git 对于公共组件那种类型的子仓库的支持并不怎么好，就是那种某个子文件夹是一个另外的 git 仓库，并被多个 git 父仓库使用的形式。实际使用的感受甚至是“糟糕透了”。&lt;/p&gt;

&lt;p&gt;这种并不友好的子仓库支持可能与 git 的设计理念有关，不过，git 的开发者始终在打补丁以稍微优化这样的体验。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;不断增加的推送时间&quot;&gt;不断增加的推送时间&lt;/h2&gt;

&lt;p&gt;如果你曾经在大仓库试过 &lt;code class=&quot;highlighter-rouge&quot;&gt;git subtree push&lt;/code&gt;，你一定为下面这张图感到抓狂：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-26-long-time-of-git-subtree.gif&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 不断增加的推送时间&lt;/p&gt;

&lt;p&gt;注意到总提交数了吗？注意到正在计算的提交数的变化了吗？你估算一下全部推送完毕需要多久？2~3 小时是跑不了的了。&lt;/p&gt;

&lt;p&gt;最令人心痛的是，等待了 2~3 个小时之后，还有机会因为 Non-Fast-Forward 而遭受拒绝。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;walterlv@LVYI MINGW64 /c/Users/OpenSource/Walterlv.Demo &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;temp/migrate&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree push &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SubFolder/Walterlv/ demo temp/from-main
git push using:  demo temp/from-main
fatal: ambiguous argument &lt;span class=&quot;s1&quot;&gt;'cb0580bb6ee76fa96f5bc3c7095303f9a33f5834^0'&lt;/span&gt;: unknown revision or path not &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the working tree.
Use &lt;span class=&quot;s1&quot;&gt;'--'&lt;/span&gt; to separate paths from revisions, like this:
&lt;span class=&quot;s1&quot;&gt;'git &amp;lt;command&amp;gt; [&amp;lt;revision&amp;gt;...] -- [&amp;lt;file&amp;gt;...]'&lt;/span&gt;
could not rev-parse &lt;span class=&quot;nb&quot;&gt;split hash &lt;/span&gt;cb0580bb6ee76fa96f5bc3c7095303f9a33f5834 from commit 691c5a1531ff38d02cb62fa34c99231dbde050b3
To gitlab.gz.cvte.cn:iip-win/cvte-paint.git
 &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;rejected]              1d3913a2e0ec6e4c507dbe2baabae18ef4b8fab9 -&amp;gt; temp/from-main &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;non-fast-forward&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
error: failed to push some refs to &lt;span class=&quot;s1&quot;&gt;'git@gitlab.gz.cvte.cn:iip-win/cvte-paint.git'&lt;/span&gt;
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;e.g. &lt;span class=&quot;s1&quot;&gt;'git pull ...'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; before pushing again.
hint: See the &lt;span class=&quot;s1&quot;&gt;'Note about fast-forwards'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'git push --help'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;details.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;永远也解不完的冲突&quot;&gt;永远也解不完的冲突&lt;/h2&gt;

&lt;p&gt;在下次执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;git subtree pull&lt;/code&gt; 的时候，不管两个仓库有什么样的新变化，只要两边的代码不一样——就是冲突。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-26-10-18-22.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;原因&quot;&gt;原因&lt;/h2&gt;

&lt;p&gt;每次执行 subtree 的 push 命令的时候，总会重新为子目录生成新的提交。然而这造成了一些很麻烦的问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;每个提交都需要重新计算，因此每次推送都需要把主仓库所有的提交计算一遍，非常耗时；&lt;/li&gt;
  &lt;li&gt;每次 push 都是重新计算的，因此本地和远端新仓库的提交总是不一样的，关键还没有共同的父级，这导致 git 无法自动为我们解决冲突。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;解决&quot;&gt;解决&lt;/h2&gt;

&lt;p&gt;git subtree 提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;split&lt;/code&gt; 命令，官方对此的描述是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Extract a new, synthetic project history from the history of the &lt;prefix&gt; subtree. The new history includes only the commits (including merges) that affected &lt;prefix&gt;, and each of those commits now has the contents of &lt;prefix&gt; at the root of the project instead of in a subdirectory. Thus, the newly created history is suitable for export as a separate git repository.&lt;/prefix&gt;&lt;/prefix&gt;&lt;/prefix&gt;&lt;/p&gt;

  &lt;p&gt;After splitting successfully, a single commit id is printed to stdout. This corresponds to the HEAD of the newly created tree, which you can manipulate however you want.&lt;/p&gt;

  &lt;p&gt;Repeated splits of exactly the same history are guaranteed to be identical (ie. to produce the same commit ids). Because of this, if you add new commits and then re-split, the new commits will be attached as commits on top of the history you generated last time, so ‘git merge’ and friends will work as expected.&lt;/p&gt;

  &lt;p&gt;Note that if you use ‘–squash’ when you merge, you should usually not just ‘–rejoin’ when you split.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意思是说，当使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;split&lt;/code&gt; 命令后，git subtree 将确保对于相同历史的分割始终是相同的提交号。&lt;/p&gt;

&lt;p&gt;于是，当需要 push 的时候，git 将只计算 split 之后的新提交；并且下次 split 的时候，以前相同的历史纪录将得到相同的 git 提交号。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git subtree &lt;span class=&quot;nb&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rejoin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Dependencies/Cvte.Paint/ HEAD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/25294227/6233938&quot;&gt;git-subtree pull merge conflict - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/16134975/6233938&quot;&gt;git - Reduce increasing time to push a subtree - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt&quot;&gt;git-subtree/git-subtree.txt at master · apenwarr/git-subtree&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:01:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/performance-of-git-subtree.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/performance-of-git-subtree.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下</title>
        <description>&lt;p&gt;我们经常会使用条件编译符 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 在 Debug 下执行某些特殊代码。但是一旦我们把代码打包成 dll，然后发布给其他小伙伴使用的时候，这样的判断就失效了，因为发布的库是 Release 配置的；那些 &lt;code class=&quot;highlighter-rouge&quot;&gt;#if DEBUG&lt;/code&gt; 的代码根本都不会编译进库中。然而总有时候希望在库中也能得知程序是 Debug 还是 Release，以便库发布之后也能在 Debug 下多做一些检查。&lt;/p&gt;

&lt;p&gt;那么有办法得知使用此库的程序是 Debug 配置还是 Release 配置下编译的呢？本文将介绍一个比较靠谱的方法（适用于 .NET Standard）。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;先上代码&quot;&gt;先上代码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.ComponentModel&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 包含在运行时判断编译器编译配置中调试信息相关的属性。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DebuggingProperties&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// 检查当前正在运行的主程序是否是在 Debug 配置下编译生成的。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsDebug&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_isDebugMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 由于调用 GetFrames 的 StackTrace 实例没有跳过任何帧，所以 GetFrames() 一定不为 null。&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debuggableAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCustomAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_isDebugMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingFlags&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnableEditAndContinue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isDebugMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isDebugMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;再解释原理&quot;&gt;再解释原理&lt;/h2&gt;

&lt;h3 id=&quot;发现特性&quot;&gt;发现特性&lt;/h3&gt;

&lt;p&gt;所有 .NET 开发者都应该知道我们编译程序时有 Debug 配置和 Release 配置，具体来说是项目文件中一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Configuration&amp;gt;&lt;/code&gt; 的节点记录的字符串。&lt;/p&gt;

&lt;p&gt;使用 Debug 编译后的程序和 Release 相比有哪些可以检测到的不同呢？我反编译了我的一个程序集。&lt;/p&gt;

&lt;p&gt;.NET Core 程序集，Debug 编译：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WrapNonExceptionThrows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Debuggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisableOptimizations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreSymbolStoreSequencePoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnableEditAndContinue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Debug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyInformationalVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyProduct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;.NET Core 程序集，Release 编译：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WrapNonExceptionThrows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Debuggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreSymbolStoreSequencePoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Release&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyInformationalVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyProduct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;发现一个很棒的特性 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyConfiguration&lt;/code&gt;，直接写明了当前是 Debug 还是 Release 编译的。&lt;/p&gt;

&lt;p&gt;你以为这就完成了？我们再来看看 .NET Framework 下面的情况。&lt;/p&gt;

&lt;p&gt;.NET Framework 程序集，Debug 编译：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WrapNonExceptionThrows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Debuggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisableOptimizations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreSymbolStoreSequencePoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnableEditAndContinue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyProduct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCopyright&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copyright © walterlv 2018&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTrademark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ComVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TargetFramework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework,Version=v4.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkDisplayName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.NET Framework 4.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;.NET Framework 程序集，Release 编译：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilationRelaxations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WrapNonExceptionThrows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Debuggable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggableAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DebuggingModes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreSymbolStoreSequencePoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyProduct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyCopyright&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copyright © walterlv 2018&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TargetFramework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework,Version=v4.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameworkDisplayName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.NET Framework 4.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;已经没有 &lt;code class=&quot;highlighter-rouge&quot;&gt;AssemblyConfiguration&lt;/code&gt; 特性可以用了。不过我们额外发现一个比较间接的特性可用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Debuggable&lt;/code&gt;，至少两者都是有的，可以写出兼容的代码。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DebuggableAttribute.DebuggingModes&lt;/code&gt; 有多个值：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;自 .NET Framework 2.0 开始，JIT 跟踪信息始终会生成，所以这个属性已经没用了。如果指定为这个值，会直接按 &lt;code class=&quot;highlighter-rouge&quot;&gt;Default&lt;/code&gt; 处理。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Default&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;允许 JIT 编译器进行优化。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DisableOptimizations&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;禁止编译器对输出程序集进行优化，因为优化可能导致调试过程非常困难。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IgnoreSymbolStoreSequencePoints&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EnableEditAndContinue&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;允许在进入断点的情况下编辑代码并继续执行。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通常在 Debug 下编译时，使用的值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnableEditAndContinue&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;寻找程序集&quot;&gt;寻找程序集&lt;/h3&gt;

&lt;p&gt;以上发现的程序集特性是需要找到一个程序集的，那么应该使用哪一个程序集呢？&lt;/p&gt;

&lt;p&gt;通常我们调试的时候是运行一个入口程序的，所以可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly()&lt;/code&gt; 来获取入口程序集。然而微软官网对此方法有一个描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The assembly that is the process executable in the default application domain, or the first executable that was executed by AppDomain.ExecuteAssembly. Can return null when called from unmanaged code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说如果入口程序集是非托管程序集，那么这个可能返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。这可能发生在单元测试中、性能测试中或者其他非托管程序调用托管代码的情况；虽然不是主要场景，却很常见。所以，我们依然需要处理返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的情况。&lt;/p&gt;

&lt;p&gt;那么如何才能找到我们需要的入口程序集呢？考虑托管代码的调用栈中的第一个函数可能是最接近使用者调试的程序集的，所以我们可以采取查找栈底的方式：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StackTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFrames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace.GetFrames()&lt;/code&gt; 方法可能返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，但那仅对于一个任意的 &lt;code class=&quot;highlighter-rouge&quot;&gt;StackTrace&lt;/code&gt;。在我们的使用场景中是取整个托管调用栈的，由于这个方法本身就是托管代码，所以栈中至少存在一个帧；也就是说此方法在我们的场景中是不可能返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 的。所以代码静态检查工具如果提示需要处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，其实是多余的担心。&lt;/p&gt;

&lt;h3 id=&quot;性能&quot;&gt;性能&lt;/h3&gt;

&lt;p&gt;另外，一个编译好的程序集是不可能在运行时再去修改 Debug 和 Release 配置的，所以第一次获取完毕后就可以缓存下来以便后续使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.reflection.assembly.getentryassembly.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;Assembly.GetEntryAssembly Method (System.Reflection)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/14165787/6233938&quot;&gt;c# - I need an alternative to &lt;code class=&quot;highlighter-rouge&quot;&gt;Assembly.GetEntryAssembly()&lt;/code&gt; that never returns null - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/diagnostics/stacktrace.cs,84f88e3b241d29e3,references&quot;&gt;StackTrace.GetFrames&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Sep 2018 00:00:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/check-running-in-debug-or-release-in-a-library.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/check-running-in-debug-or-release-in-a-library.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何精准地用打印机在贺卡或邀请函上打字</title>
        <description>&lt;p&gt;当你有一堆带有空位的贺卡或邀请函，你是准备手写呢，还是准备打字呢？你的旁边恰巧有一位书法漂亮的 MM 那自不必说，然而如果字都像我这样写得丑，那还是选择打字好了。&lt;/p&gt;

&lt;p&gt;然而，贺卡或邀请函不同于白纸，通常都是已经打印好有内容的。那么如何打印以便让所有文字的内容都对应到贺卡或邀请函的正确空位上，还是有些难度的。&lt;/p&gt;

&lt;p&gt;本文将教你如何在毫米级别将文字精准地打印到贺卡或邀请函上。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;准备工具&quot;&gt;准备：工具&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;你的贺卡或邀请函（为了描述方便，后面都称作邀请函，需要至少一张用来试验）&lt;/li&gt;
  &lt;li&gt;一个带有 Microsoft Word 或其他文档编辑软件的 PC&lt;/li&gt;
  &lt;li&gt;一把具有 1 ms 精度的尺子&lt;/li&gt;
  &lt;li&gt;一个打印机（那种不会将纸卷起来的类型）&lt;/li&gt;
  &lt;li&gt;一张 A4 纸&lt;/li&gt;
  &lt;li&gt;一支笔&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;第一步准备打印机和纸张&quot;&gt;第一步：准备打印机和纸张&lt;/h2&gt;

&lt;p&gt;取一张 A4 纸，将邀请函放置于 A4 纸的任意一角。无需关心到底是哪一个角，因为不同打印机打印的方向并不相同，我们马上会进行实验判断到底应该放到哪一角。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-24-08-55-33.png&quot; alt=&quot;对齐邀请函&quot; /&gt;&lt;br /&gt;
▲ 对齐邀请函&lt;/p&gt;

&lt;p&gt;用尺子将邀请函在 A4 纸上的轮廓勾勒出来：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-24-08-55-42.png&quot; alt=&quot;勾勒轮廓&quot; /&gt;&lt;br /&gt;
▲ 勾勒轮廓&lt;/p&gt;

&lt;p&gt;然后取下邀请函，将其放入打印机中：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-24-15-08-21.png&quot; alt=&quot;放入打印机&quot; /&gt;&lt;br /&gt;
▲ 放入打印机&lt;/p&gt;

&lt;h2 id=&quot;第二步制作-word-文档&quot;&gt;第二步：制作 Word 文档&lt;/h2&gt;

&lt;p&gt;在 Microsoft Word 中新建一个文档，需要进行一些设置：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;纸张大小：A4&lt;/li&gt;
  &lt;li&gt;纸张方向：横向&lt;/li&gt;
  &lt;li&gt;文档边距：0（注意这里的上下左右四个方向都要设置为 0）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了能够更精准地控制打印文字地位置，我们还需要在 Word 中做一些标尺设置：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;文件 → 选项 → 高级
    &lt;ul&gt;
      &lt;li&gt;标尺单位设为“厘米”&lt;/li&gt;
      &lt;li&gt;取消勾选“使用字符单位” （&lt;strong&gt;重要&lt;/strong&gt;！不然“厘米”单位依然不会生效的）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;视图
    &lt;ul&gt;
      &lt;li&gt;勾选标尺&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;接下来，我们就不会再在 Word 文档中直接打字了，而是全部使用文本框。&lt;/p&gt;

&lt;p&gt;使用标尺，我们可以大致估算出邀请函占用下面这么多的空间：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-13-46-47.png&quot; alt=&quot;估算邀请函占用的空间&quot; /&gt;&lt;br /&gt;
▲ 估算邀请函占用的空间&lt;/p&gt;

&lt;p&gt;于是我们在邀请函的文本中间放置一个文本框和随意的内容：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-13-45-53.png&quot; alt=&quot;随意的文本框&quot; /&gt;&lt;br /&gt;
▲ 随意的文本框&lt;/p&gt;

&lt;p&gt;现在打印测试效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-13-52-03.png&quot; alt=&quot;打印看效果&quot; /&gt;&lt;br /&gt;
▲ 打印看效果&lt;/p&gt;

&lt;p&gt;很不幸，打印出来与我们预期的方向是反着的。我们之前并没有使用真实的邀请函测试，而是勾勒出了轮廓 —— 这就是为了测试打印机的打印方向二准备的！&lt;/p&gt;

&lt;h2 id=&quot;第三步调整打印方向&quot;&gt;第三步：调整打印方向&lt;/h2&gt;

&lt;p&gt;如果你上一步得到的方向是对着的，那么这整个第三步都是可以忽略的。&lt;/p&gt;

&lt;p&gt;现在，我们要么调整文档方向，要么调整打印机纸张方向。为了之后对齐文字的直观，我们当然应该选择调整打印机的纸张方向。&lt;/p&gt;

&lt;p&gt;于是，请重新将刚刚的打印纸放回打印机，但是我们预期的邀请函的方向就变化了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-rotate-card.gif&quot; alt=&quot;预期的邀请函方向发生了变化&quot; /&gt;&lt;br /&gt;
▲ 预期的邀请函方向发生了变化&lt;/p&gt;

&lt;p&gt;也就是说，我们的 Word 文档中文字应该从右上角区域移至左下角区域：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-move-word-area.gif&quot; alt=&quot;Word 中的编辑区域也应相应改变&quot; /&gt;&lt;br /&gt;
▲ Word 中的编辑区域也应相应改变&lt;/p&gt;

&lt;p&gt;于是，我们的准备工作就算结束了。以后对于同款打印机，你可以不用再尝试了，直接使用这一次的调试结果：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;记住这次邀请函在纸张中的位置和方向&lt;/li&gt;
  &lt;li&gt;记住这次 Word 文档中邀请函的位置&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;精确对齐邀请函中的占位符&quot;&gt;精确对齐邀请函中的占位符&lt;/h2&gt;

&lt;p&gt;现在，准备好你的尺子，把邀请函放到打印纸上，你需要初步量出每个占位符的位置，以便在 Word 文档中相应的位置插入文本框。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-14-48-19.png&quot; alt=&quot;测量出每一个占位符的位置&quot; /&gt;&lt;br /&gt;
▲ 测量出每一个占位符的位置&lt;/p&gt;

&lt;p&gt;在测量的过程中，不断对应到 Word 文档的相应位置。（注意到我们一开始打开了 Word 文档的厘米标注吗？就是为了与物理尺寸对齐。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-14-57-25.png&quot; alt=&quot;在 Word 文档中创建文本框&quot; /&gt;&lt;br /&gt;
▲ 在 Word 文档中创建文本框&lt;/p&gt;

&lt;p&gt;现在，将用来试验的邀请函放入打印机中，我们即将直接在邀请函中进行试验。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-04-03.png&quot; alt=&quot;将试验邀请函放入打印机&quot; /&gt;&lt;br /&gt;
▲ 将试验邀请函放入打印机&lt;/p&gt;

&lt;p&gt;通常，第一次打印出来的效果不尽如人意，即便你测量得非常精确可能都会如此：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-06-07.png&quot; alt=&quot;不尽如人意的打印效果&quot; /&gt;&lt;br /&gt;
▲ 不尽如人意的打印效果&lt;/p&gt;

&lt;p&gt;不过不要紧，你只需要测量实际打印位置（大小）和实际所需位置之间的差异，得知你的每一个文本框需要分别向上下左右移动多少毫米，然后在 Word 文档中将文本框移动那么长的距离。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;一个小技巧：你可以尝试找出按一次方向键对应移动多少毫米，这样可以迅速找到精确的移动毫米数。（比如我实际尝试在 100% 缩放比下，3 次方向键移动 1 毫米。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;不断在刚刚已经打印过的邀请函上打印试错，直至所有文本框都得到满意的位置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-11-33.png&quot; alt=&quot;所有的文本框都到达满意的位置&quot; /&gt;&lt;br /&gt;
▲ 所有的文本框都到达满意的位置&lt;/p&gt;

&lt;h2 id=&quot;打印最终邀请函&quot;&gt;打印最终邀请函&lt;/h2&gt;

&lt;p&gt;由于你所有的文本框已对齐，所以即便你再拿一个全新的邀请函放入打印机打印，也依然能得到精确对齐的打印版邀请函。所以你只需要填入最终信息打印即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-26-15-30-05.png&quot; alt=&quot;最终打印的邀请函&quot; /&gt;&lt;br /&gt;
▲ 最终打印的邀请函&lt;/p&gt;

&lt;h2 id=&quot;邮件合并&quot;&gt;邮件合并&lt;/h2&gt;

&lt;p&gt;然而，你既然希望打印，邀请函肯定不止发送给一个人。&lt;/p&gt;

&lt;p&gt;人数较少则无所谓，你可以每打印一张改一下名字。然而人数一旦多起来，这种方案肯定让你吐血！&lt;/p&gt;

&lt;p&gt;Word 中有“邮件合并”功能，你可以在 Word 中插入“域”，然后导入 Excel 名单为每一个人生成专属的邀请函。&lt;/p&gt;

&lt;p&gt;关于“邮件合并”功能，你可以阅读：&lt;a href=&quot;https://support.office.com/zh-cn/article/%E4%BD%BF%E7%94%A8-excel-%E7%94%B5%E5%AD%90%E8%A1%A8%E6%A0%BC%E8%BF%9B%E8%A1%8C%E9%82%AE%E4%BB%B6%E5%90%88%E5%B9%B6-858c7d7f-5cc0-4ba1-9a7b-0a948fa3d7d3&quot;&gt;使用 Excel 电子表格进行邮件合并 - Word&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Aug 2018 07:35:47 +0000</pubDate>
        <link>https://blog.walterlv.com/post/print-to-fill-words-in-card.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/print-to-fill-words-in-card.html</guid>
        
        
        <category>office</category>
        
      </item>
    
      <item>
        <title>通过解读 WPF 触摸源码，分析 WPF 插拔设备触摸失效的问题（问题篇）</title>
        <description>&lt;p&gt;在 .NET Framework 4.7 以前，WPF 程序的触摸处理是基于操作系统组件但又自成一套的，这其实也为其各种各样的触摸失效问题埋下了伏笔。再加上它出现得比较早，触摸失效问题也变得更加难以解决。即便是 .NET Framework 4.7 以后也需要开发者手动开启 &lt;code class=&quot;highlighter-rouge&quot;&gt;Pointer&lt;/code&gt; 消息，并且存在兼容性问题。&lt;/p&gt;

&lt;p&gt;本文将通过解读 WPF 触摸部分的源码，分析 WPF 插拔设备触摸失效的问题。随后，会给微软报这个 Bug。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/wpf-touch-fails-when-tablet-device-changed.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/wpf-touch-fails-when-tablet-device-changed-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;所谓“触摸失效”，指的是无论你如何使用手指或触摸笔在触摸屏上书写、交互，程序都没有任何反应。而使用鼠标操作则能正常使用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;本文所述的“触摸失效问题”我在 &lt;a href=&quot;/wpf/2017/09/12/touch-not-work-in-wpf.html&quot;&gt;WPF 程序无法触摸操作&lt;/a&gt; 一文中有所提及，但本文偏向于分析其内部发生的原因。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;本文与 &lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 的 &lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E6%8F%92%E6%8B%94%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88.html&quot;&gt;WPF 插拔触摸设备触摸失效&lt;/a&gt; 所述的是同一个问题。那篇文章会更多的偏向于源码解读，而本文更多地偏向于分析触摸失效的过程。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;wpf-程序插拔设备导致触摸失效问题&quot;&gt;WPF 程序插拔设备导致触摸失效问题&lt;/h2&gt;

&lt;p&gt;无论你写的 WPF 程序多么简单，哪怕只有一个最简单的窗口带着一个可以交互的按钮，本文所述的触摸失效问题你都可能遇到。&lt;/p&gt;

&lt;p&gt;具体需要的条件为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;运行 &lt;strong&gt;任意的 WPF 程序&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;插拔带有触摸的 HID 设备&lt;/strong&gt;（可以是物理插拔，也可以是驱动或软件层面的插拔）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以上虽说是必要条件，但如果要提高触摸失效的复现概率，需要制造一个较高的 CPU 占用：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当前系统中有 &lt;strong&gt;较高的 CPU 占用率&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可能还有一些尚不确定的条件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;是否对 .NET Framework 的版本有要求？&lt;/li&gt;
  &lt;li&gt;是否对 Windows 操作系统的版本有要求？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;将以上所有条件组合起来，对于触摸失效的问题描述为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当运行任意的 WPF 程序时，如果此时操作系统有较高的 CPU 占用，并且此时存在带有触摸的 HID 设备插拔，那么此 WPF 程序可能出现“触摸失效”问题，即此后此程序再也无法触摸操作了。&lt;/li&gt;
  &lt;li&gt;如果此时系统中同时运行了多个 WPF 程序，多个 WPF 程序可能都会在此时出现触摸失效问题。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;触摸失效原因初步分析&quot;&gt;触摸失效原因初步分析&lt;/h2&gt;

&lt;p&gt;WPF 从收集设备触摸到大多数开发者所熟知的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Stylus&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse&lt;/code&gt; 事件需要两个不同的线程完成。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;主线程，负责进行 Windows 消息循环&lt;/li&gt;
  &lt;li&gt;StylusInput 线程，负责从 WPF 非托管代码和 COM 组件中获得触摸信息&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;主线程中的 Windows 消息循环处理这些消息：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;LBUTTONDOWN, LBUTTONUP&lt;/li&gt;
  &lt;li&gt;DEVICECHANGE, TABLETADDED, TABLETREMOVED&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stylus Input 线程主要由 &lt;code class=&quot;highlighter-rouge&quot;&gt;PenThreadWorker&lt;/code&gt; 类创建，在线程循环中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEvent&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; 这两个函数来获取整个触摸设备中的触摸事件，并将触摸的原始信息向 WPF 的其他触摸处理模块传递。传递的其中一个模块是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkerOperationGetTabletsInfo&lt;/code&gt; 类，其的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDoWork&lt;/code&gt; 方法中会通过 COM 组件获取触摸设备个数。&lt;/p&gt;

&lt;p&gt;而导致触摸失效的错误代码就发生在以上 Stylus Input 线程的处理中。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PenThreadWorker&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; 方法传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;_handles&lt;/code&gt; 为空数组，这会导致进行无限的等待。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WorkerOperationGetTabletsInfo&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDoWork&lt;/code&gt; 因为 COM 组件错误出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;COMException&lt;/code&gt; 或因为线程安全问题出现 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt;；此时方法内部会 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 然后返回空数组，这使得即时存在触摸设备也会因此而识别为不存在。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了方便理解以上的两个 Bug，可以看看我简化后的 .NET Framework 源码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// PenThreadWorker.ThreadProc&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;这里是两层循环，简化成一个以便理解&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 以下的 break 都只退出一层循环而已。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPenEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;其他参数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPenEventMultiple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;其他参数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 后续逻辑。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// WorkerOperationGetTabletsInfo.OnDoWork&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDeviceInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PenThreadWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTabletInfoHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pimcTablet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COMException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDevicesInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDevicesInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 其他异常。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的问题分析中，&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt; 异常几乎可以肯定是线程安全问题所致；&lt;code class=&quot;highlighter-rouge&quot;&gt;COMException&lt;/code&gt; 不能确定；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; 中的参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;handles&lt;/code&gt; 实际上是用来进行非托管和托管代码线程同步用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ResetEvent&lt;/code&gt; 集合，所以实际上也是线程同步问题导致的死锁。&lt;/p&gt;

&lt;p&gt;同时联系以上必要复现步骤中，如果当前存在高 CPU 占用则可以大大提高复现概率；我们几乎可以推断，此问题是 WPF 对触摸的处理存在线程安全的隐患所致。&lt;/p&gt;

&lt;h2 id=&quot;此触摸失效问题的解决方法&quot;&gt;此触摸失效问题的解决方法&lt;/h2&gt;

&lt;p&gt;在推断出初步原因后，根本的解决方法其实只剩下两个了：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;修复 WPF 的 Bug
    &lt;ul&gt;
      &lt;li&gt;由于我们无法编译 .NET Framework 的源码，所以几乎只能由微软来修复这个 Bug，即需要新版本的 WPF 来解决这个线程安全隐患&lt;/li&gt;
      &lt;li&gt;当然，此问题的修复可以跟随 .NET Framework 更新，也可以跟随即将推出的 .NET Core 3 进行更新。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;更新 Windows（传说中的补丁）
    &lt;ul&gt;
      &lt;li&gt;新的 Windows 提供给 WPF 的 COM 组件可能也需要修复线程安全或其他与触摸硬件相关的问题&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;比较彻底的方案是以上两者都需要修复，但都 &lt;strong&gt;只能由微软来完成&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;那我们非微软开发者可以做些什么呢？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;降低 CPU 占用率
    &lt;ul&gt;
      &lt;li&gt;虽然这不由我们控制，不过我们如果能降低一些意料之外的高 CPU 占用，则可以大幅降低 WPF 触摸失效问题出现的概率。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然而作为用户又可以做些什么呢？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;重新插拔触摸设备（如果你的触摸框是通过 USB 连接可以手工插拔的话）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;触摸失效问题的分析过程&quot;&gt;触摸失效问题的分析过程&lt;/h2&gt;

&lt;p&gt;以上结论的得出，离不开对 .NET Framework 源码的解读和调试。&lt;/p&gt;

&lt;p&gt;由于 WPF 的触摸原理涉及到较多类型和源码，需要大量篇幅描述，所以不在本文中说明。阅读以下文章可以更加深入地了解这个触摸失效的问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E6%8F%92%E6%8B%94%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88.html&quot;&gt;WPF 插拔触摸设备触摸失效 - lindexi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analyze-wpf-losting-touch-when-tablet-device-changed&quot;&gt;通过解读 WPF 触摸源码，分析 WPF 插拔设备触摸失效的问题（分析篇） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文所有的 .NET Framework 源码均由 &lt;a href=&quot;https://github.com/0xd4d/dnSpy&quot;&gt;dnSpy&lt;/a&gt; 反编译得出，分析过程也基本是借助 dnSpy 的无 pdb 调试特性进行。关于 dnSpy 的更多使用，可以阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95-Windows-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;断点调试 Windows 源代码 - lindexi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/edit-and-recompile-assembly-using-dnspy&quot;&gt;神器如 dnSpy，无需源码也能修改 .NET 程序 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 19 Aug 2018 11:09:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-touch-fails-when-tablet-device-changed.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-touch-fails-when-tablet-device-changed.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>为修改了链接地址的博客进行重定向</title>
        <description>&lt;p&gt;不同于笔记，博客除了给自己带来知识的积累之外，还将知识和思想分享给了互联网上的同道中人。&lt;/p&gt;

&lt;p&gt;于是，当我不得不修改博客地址的时候，就不得不考虑地址修改的兼容问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;博客自发布的那一刻开始，就随时可能被各种奇怪的机构收录：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;搜索引擎&lt;em&gt;（喜欢被收录，这样就有更多的人能够获益）&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;授权的转载站点&lt;em&gt;（虽然目前还没有）&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;各种泛滥的去除了原作者信息的盗版&lt;em&gt;（比如这里 &lt;a href=&quot;http://www.qingpingshan.com/bc/aspnet/334582.html&quot;&gt;在Windows10系统上为WPF窗口添加模糊特效&lt;/a&gt;）&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;被其它内容引用&lt;em&gt;（喜欢被引用，说明这份知识是有用的）&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;分享和开放的互联网行为一般会在引用或收录的时候加上原文链接，于是我的链接一旦发布，便不建议再更改。&lt;/p&gt;

&lt;p&gt;可是，链接有问题啊！那就重定向！&lt;/p&gt;

&lt;p&gt;我使用 Jekyll 博客，于是，我在根目录建立了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;redirect&lt;/code&gt; 文件夹，专门存放链接的重定向。里面的内容只有两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;存放原址&lt;/li&gt;
  &lt;li&gt;重定向到目标地址的脚本&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;代码如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
permalink: /post/wpf-add-on-ui.html
---
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  window.location.href=&quot;/post/wpf-cross-domain-ui.html&quot;;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以在这个链接中尝试重定向：&lt;a href=&quot;https://walterlv.github.io/post/wpf-add-on-ui.html&quot;&gt;https://walterlv.github.io/post/wpf-add-on-ui.html&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;附那些盗版&quot;&gt;附那些盗版&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-14-00-58-47.png&quot; alt=&quot;盗版&quot; /&gt;&lt;br /&gt;
▲ 某掐头去尾的盗版网站&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-14-00-54-54.png&quot; alt=&quot;盗版&quot; /&gt;&lt;br /&gt;
▲ 盗版&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-14-00-54-30.png&quot; alt=&quot;被翻译了的盗版&quot; /&gt;&lt;br /&gt;
▲ 被翻译了的盗版&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-14-00-51-28.png&quot; alt=&quot;被翻译了的盗版&quot; /&gt;&lt;br /&gt;
▲ 被翻译了的盗版&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Aug 2018 11:09:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/redirect-for-blog-links.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/redirect-for-blog-links.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>.NET 中 GetProcess 相关方法的性能</title>
        <description>&lt;p&gt;.NET 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process&lt;/code&gt; 类中提供了查找进程的若干方法，其中部分方法还比较消耗性能。如果你试图优化查找进程相关方法的性能，可能本文分享的一些耗时数据可以作为参考。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;性能比较&quot;&gt;性能比较&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process&lt;/code&gt; 类中提供了四种查询进程的方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcesses&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;获取当前计算机或远程计算机上运行的所有进程。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessById&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;获取当前计算机或远程计算机上 pid 为 指定值的进程。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessesByName&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;根据进程的名字查找当前计算机或远程计算机上的进程。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetCurrentProcess&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;获取当前进程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process&lt;/code&gt; 实例。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;先给出我的实测数据（100 次执行耗时）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetProcesses()&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;00:00:00.7254688&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetProcessById(id)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;00:00:01.3660640（实际数值取决于当前进程数）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetProcessesByName(&quot;Walterlv.Demo&quot;)&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;00:00:00.5604279&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetCurrentProcess()&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;00:00:00.0000546&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;结果显示获取所有进程实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcesses&lt;/code&gt; 方法速度竟然比获取单个进程实例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessById&lt;/code&gt; 还要快得多！额外地，根据名称查找进程比前两者都快，获取当前进程实例的方法快得不是一个数量级。&lt;/p&gt;

&lt;h2 id=&quot;这些速度差异源于哪里&quot;&gt;这些速度差异源于哪里&lt;/h2&gt;

&lt;p&gt;我们先看看最慢的方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessIds&lt;/code&gt;，它的最本质的实现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessManager&lt;/code&gt; 类中：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// ProcessManager&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProcessIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(;;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumProcesses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Win32Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ids&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先创建一个 256 长度的数组，然后使用本机函数枚举进程列表填充这个数组。如果实际所需的数组大小与传入的数组大小相等，说明数组用完了，有可能进程数比 256 个多。所以，将数组长度扩大为两倍，随后再试一次。直到发现申请的数组长度足够存下进程数为止。&lt;/p&gt;

&lt;p&gt;这里用到了本机方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;EnumProcesses&lt;/code&gt; 来枚举进程。传入的 &lt;code class=&quot;highlighter-rouge&quot;&gt;size&lt;/code&gt; 要乘以 4 是因为传入的是字节数，一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; 是 4 个字节。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// NativeMethods&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;psapi.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnumProcesses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;needed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以我们可以得知，如果当前计算机中的进程数小于 256 个，那么枚举进程方法仅需执行一次；而如果大于或等于 256 个，则枚举进程的方法需要执行两次或更多次，这是性能很差的一个重要原因。&lt;/p&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcesses&lt;/code&gt; 方法就要复杂得多，其核心调用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessManager.GetProcessInfos&lt;/code&gt; 方法。方法很长，但其大体思路是获取当前计算机上的线程列表，然后将线程所在的进程储存到哈希表中（相当于去重），随后返回此哈希表的数组副本。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// ProcessManager&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProcessInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProcessInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processIdFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 60 is a reasonable number for processes on a normal machine.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Hashtable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hashtable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalOffset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SystemProcessInformation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SystemProcessInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStructure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfoProcessId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniqueProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processIdFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;processIdFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processInfoProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// get information for a process&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ProcessInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfoProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handleCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sessionId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SessionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;poolPagedBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QuotaPagedPoolUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;poolNonpagedBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QuotaNonPagedPoolUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtualBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VirtualSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtualBytesPeak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PeakVirtualSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workingSetPeak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PeakWorkingSetSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workingSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkingSetSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pageFileBytesPeak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PeakPagefileUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pageFileBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PagefileUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;privateBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrivatePageCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basePriority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BasePriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;


            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NamePtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                    
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NtProcessManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemProcessID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;System&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NtProcessManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IdleProcessID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Idle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                    &lt;span class=&quot;c1&quot;&gt;// for normal process without name, using the process ID. &lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                     
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProcessShortName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStringUni&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NamePtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;  
                &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// On old operating system (NT4 and windows 2000), the process name might be truncated to 15 &lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// characters. For example, aspnet_admin.exe will show up in performance counter as aspnet_admin.ex.&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Process class try to return a nicer name. We used to get the main module name for a process and &lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// use that as the process name. However normal user doesn't have access to module information, &lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// so normal user will see an exception when we try to get a truncated process name.&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//                    &lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsOSOlderThanXP&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.ex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                          
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// get the threads for current process&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;processInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NumberOfThreads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SystemThreadInformation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SystemThreadInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PtrToStructure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                    
                &lt;span class=&quot;n&quot;&gt;ThreadInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThreadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                    

                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniqueProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniqueThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basePriority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BasePriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentPriority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadWaitReason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NtProcessManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetThreadWaitReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WaitReason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;processInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadInfoList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentPtr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ti&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NextEntryOffset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;totalOffset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NextEntryOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ProcessInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProcessInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessesByName&lt;/code&gt; 方法就比较奇怪了，因为其本质上就是调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Process.GetProcesses&lt;/code&gt; 方法，并在其后额外执行了一些代码。理论上不应该出现耗时更短的情况。事实上，在测试中，我将 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcesses&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessesByName&lt;/code&gt; 方法的执行调换顺序也能得到稳定一致的结果，都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetProcessesByName&lt;/code&gt; 更快。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProcessesByName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;machineName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;procs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProcesses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;machineName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ArrayList&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;procs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;procs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;procs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;                    
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;procs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CopyTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetCurrentProcess&lt;/code&gt; 方法能够这么快，很好理解，毕竟是自己进程，有什么拿不到的呢？其内部调用的是本机方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kernel32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InteropServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCurrentProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，有个有意思的现象：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.oschina.net/question/23734_29378&quot;&gt;Windows的PID为什么是4的倍数 - 开源中国社区&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/Thriving-Country/archive/2011/09/18/2180143.html&quot;&gt;WINDOWS进程或线程号为什么是4的倍数 - GUO Xingwang - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 19 Aug 2018 07:04:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/performance-of-get-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/performance-of-get-process.html</guid>
        
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>WPF Applications Stop Responding to Touches after Adding or Removing Tablet Devices</title>
        <description>&lt;p&gt;WPF framework handles touch devices and events mostly using its own code and COM components instead of using the windows message loop. Unfortunately, there may be some bugs in the WPF touch handling codes. So we sometimes suffer from the WPF touch failures. This changes after Microsoft introducing .NET Framework 4.7, but the developers have to switch on the &lt;code class=&quot;highlighter-rouge&quot;&gt;Pointer&lt;/code&gt; message manually with some compliant issues.&lt;/p&gt;

&lt;p&gt;In this article, I’ll post some codes of WPF to present its potential bugs of touch failure.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/wpf-touch-fails-when-tablet-device-changed.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/wpf-touch-fails-when-tablet-device-changed-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-touch-failure-issue&quot;&gt;The touch failure issue&lt;/h2&gt;

&lt;p&gt;Even if you write a very simple WPF application which contains only a button, you’ll be suffering from the touch failure issue.&lt;/p&gt;

&lt;p&gt;What you need is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run any &lt;strong&gt;WPF application&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Keep plugging and unplugging a USB HID tablet device&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The actions above helps reproduce touch failure with a small probability. But if you want a larger probability, you should:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make a high CPU usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Probably there may be other conditions such as the .NET Framework version and the Windows version but I’m not sure.&lt;/p&gt;

&lt;p&gt;When you put them together, you’ll get a full touch failure issue description.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Run any WPF application with a high CPU usage, and then keep plugging and unplugging a USB HID tablet device, you’ll get the WPF application which stops responding to touches.&lt;/li&gt;
  &lt;li&gt;If multiple WPF applications are running at the same time, most of them will lose touch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;preliminary-analysis-of-the-touch-failure&quot;&gt;Preliminary analysis of the touch failure&lt;/h2&gt;

&lt;p&gt;WPF use two different threads to collect touch information from tablet devices and convert them to the &lt;code class=&quot;highlighter-rouge&quot;&gt;Stylus&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse&lt;/code&gt; event that most of us are familiar to.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The &lt;em&gt;Main&lt;/em&gt; thread of WPF. WPF use windows message loop on this thread to handle mouse message and device change message.&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;Stylus Input&lt;/em&gt; thread. WPF run the unmanaged code and call COM component to collect device information and touch information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The WPF stylus code uses the windows message loop to handle these messages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;LBUTTONDOWN, LBUTTONUP&lt;/li&gt;
  &lt;li&gt;DEVICECHANGE, TABLETADDED, TABLETREMOVED&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;Stylus Input&lt;/em&gt; thread is created by the &lt;code class=&quot;highlighter-rouge&quot;&gt;PenThreadWorker&lt;/code&gt; class. The &lt;code class=&quot;highlighter-rouge&quot;&gt;PenThreadWorker&lt;/code&gt; call &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEvent&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; in the thread loop to fetch the whole touch events of tablet devices and then it will pass the raw touch data to other touch modules to translate them into regular Stylus/Touch/Mouse events. One of the touch modules is the &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkerOperationGetTabletsInfo&lt;/code&gt; class which contains an &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDoWork&lt;/code&gt; method to fetch tablet device count through COM components.&lt;/p&gt;

&lt;p&gt;The touch failure comes from the code of the Stylus Input thread.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;An empty &lt;code class=&quot;highlighter-rouge&quot;&gt;_handles&lt;/code&gt; array is passed into the &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; method of &lt;code class=&quot;highlighter-rouge&quot;&gt;PenThreadWorker&lt;/code&gt; and this action may cause infinite waiting.&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;highlighter-rouge&quot;&gt;COMException&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt; may happen when the &lt;code class=&quot;highlighter-rouge&quot;&gt;OnDoWork&lt;/code&gt; of &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkerOperationGetTabletsInfo&lt;/code&gt; is running. This method will catch the exceptions and returns an empty array which will cause the WPF application get empty tablet devices by mistake even if there are tablet devices in actual.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve simplified the core .NET Framework code of the stylus handling above. You may understand what I mean more clearly by reading these codes:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// PenThreadWorker.ThreadProc&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;There&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loops&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// The `break` below only exit one loop, not two.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPenEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPenEventMultiple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_handles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Other logics.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// WorkerOperationGetTabletsInfo.OnDoWork&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDeviceInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PenThreadWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTabletInfoHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pimcTablet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COMException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDevicesInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_tabletDevicesInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Other exception handling.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I can definitely sure that the &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt; is the result of the thread-safety issue but I’m not sure whether &lt;code class=&quot;highlighter-rouge&quot;&gt;COMException&lt;/code&gt; is the same. The &lt;code class=&quot;highlighter-rouge&quot;&gt;handles&lt;/code&gt; argument is the handles of &lt;code class=&quot;highlighter-rouge&quot;&gt;ResetEvent&lt;/code&gt;s which are used for the thread syncing between managed code and unmanaged code. So the infinite waiting of &lt;code class=&quot;highlighter-rouge&quot;&gt;GetPenEventMultiple&lt;/code&gt; is actually a deadlock which is also a thread-safety issue.&lt;/p&gt;

&lt;p&gt;Remember that we can make a high CPU usage to increase the probability of reproduction, we can infer that the touch failure issue is caused by the thread-safety issue of WPF stylus handling.&lt;/p&gt;

&lt;h2 id=&quot;the-solutions-to-the-touch-failure&quot;&gt;The solutions to the touch failure&lt;/h2&gt;

&lt;p&gt;After inferring the preliminary reason, there are only two fundamental solutions left for us:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Fix the bug of WPF
    &lt;ul&gt;
      &lt;li&gt;Only Microsoft can fix this kind of bugs because we cannot rebuild WPF all by our selves.&lt;/li&gt;
      &lt;li&gt;Of course, this kind of patch can be introduced in .NET Framework, or be introduced in the WPF on .NET Core 3.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Fix the bug of Windows
    &lt;ul&gt;
      &lt;li&gt;The COM components provided for WPF may need an update to fix the thread-safety issue or other tablet devices issues.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A more thorough solution is that both of them need to be fixed, but &lt;strong&gt;both can only be done by Microsoft&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So what can we non-Microsoft developers do?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Reduce CPU usage
    &lt;ul&gt;
      &lt;li&gt;Although this is not controlled by us, if we can reduce some unexpected high CPU usage, we can greatly reduce the probability of WPF touch failure.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But what can I do if I’m only a normal user?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Re-plug the touch device (if your touch frame can be manually plugged in via USB connection)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-details-analysis-for-the-touch-failure&quot;&gt;The details analysis for the touch failure&lt;/h2&gt;

&lt;p&gt;The above conclusions come from the reading and debugging of the .NET Framework source code.&lt;/p&gt;

&lt;p&gt;Since WPF touch details involve more types and source code which requires a lot of descriptions, so it is not explained in this article. Read the following article to get a deeper understanding of the touch failure (all of the links are under translating):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E6%8F%92%E6%8B%94%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88.html&quot;&gt;WPF 插拔触摸设备触摸失效 - lindexi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analyze-wpf-losting-touch-when-tablet-device-changed&quot;&gt;通过解读 WPF 触摸源码，分析 WPF 插拔设备触摸失效的问题（分析篇） - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the .NET Framework source code in this article is decompiled by &lt;a href=&quot;https://github.com/0xd4d/dnSpy&quot;&gt;dnSpy&lt;/a&gt;, and the analysis process is basically based on the dnSpy’s no-PDB debugging feature.&lt;/p&gt;
</description>
        <pubDate>Wed, 15 Aug 2018 07:42:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-touch-fails-when-tablet-device-changed-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-touch-fails-when-tablet-device-changed-en.html</guid>
        
        
        <category>wpf</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>WPF 绘制对齐像素的清晰显示的线条</title>
        <description>&lt;p&gt;此前有小伙伴询问我为何他 1 像素的线条显示发虚，然后我告诉他是“像素对齐”的问题，然而他设置了各种对齐像素的属性依旧没有作用。于是我对此进行了一系列试验，对 WPF 像素对齐的各种方法进行了一次总结。此后在 StackOverflow 中，我回答了 &lt;a href=&quot;https://stackoverflow.com/questions/6018106/wpf-drawingcontext-seems-ignore-snaptodevicepixels&quot;&gt;graphics - WPF DrawingContext seems ignore SnapToDevicePixels - Stack Overflow&lt;/a&gt; 问题。&lt;/p&gt;

&lt;p&gt;阅读本文，我们将了解解决 WPF 像素对齐的四种方法以及其各自的适用范围和副作用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://r302.cc/B99pXz&quot;&gt;&lt;img src=&quot;/static/posts/2018-05-25-12-51-28.png&quot; alt=&quot;像素对齐&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;为什么要做像素对齐&quot;&gt;为什么要做像素对齐&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-19-48-01.png&quot; alt=&quot;我们在解决什么问题&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看线条！这是 3 像素的线条：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-19-44-51.png&quot; alt=&quot;线条发虚&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而论其原因，就是&lt;strong&gt;因为我们屏幕太渣&lt;/strong&gt;~哦~不，是&lt;strong&gt;因为绘制的线条没有与屏幕像素对齐&lt;/strong&gt;，具体来说是视觉对象（&lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;）的位置不在整数像素上或尺寸不是整数像素。而与此同时屏幕的点距又太大以至于我们看出来绘制的线条和屏幕像素之间的差异。&lt;/p&gt;

&lt;p&gt;然而为什么 WPF 不默认为我们对齐像素呢？这是因为要对齐像素必定带来尺寸上的偏差；这是绘制尺寸精度和最终呈现效果之间的平衡。在 MacBook、Surface Pro 这些高档显示屏上，根本不用管这样的平衡问题；但在渣渣显示器上，微软把这种平衡的控制交给了应用的开发者。&lt;/p&gt;

&lt;h2 id=&quot;处理像素对齐的四种方法&quot;&gt;处理像素对齐的四种方法&lt;/h2&gt;

&lt;h3 id=&quot;方法一布局取整-uselayoutrounding&quot;&gt;方法一：布局取整 UseLayoutRounding&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-20-40-06.png&quot; alt=&quot;UseLayoutRounding&quot; /&gt;&lt;/p&gt;

&lt;p&gt;实际效果是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-19-59-53.png&quot; alt=&quot;UseLayoutRounding 的效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-20-43-44.png&quot; alt=&quot;逗我&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;根本就不起作用&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;事实上我们从 .NET Framework 源码可以得知，&lt;code class=&quot;highlighter-rouge&quot;&gt;UseLayoutRounding&lt;/code&gt; 实际只处理 UI 元素对自己子级控件的布局取整。一旦整棵布局树种有任何一个不是整数（或者 DPI 相乘后不是整数），那么就依然没有解决问题。&lt;/p&gt;

&lt;h3 id=&quot;方法二对齐设备像素-snapstodevicepixels&quot;&gt;方法二：对齐设备像素 SnapsToDevicePixels&lt;/h3&gt;

&lt;p&gt;这是一个会沿着逻辑树继承的属性，只要最顶层设置了这个属性，里面的元素都会具备此特性。不过，他只处理矩形的渲染，也就是说，只对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Rectangle&lt;/code&gt; 这些类型的元素生效，其他的包括自己写的元素基本都是不管用的。&lt;/p&gt;

&lt;p&gt;它有一个好处，是像素对齐的情况下同时能够保证显示不足或超过 1 像素时，也能带一点儿透明或者超过一点像素。&lt;/p&gt;

&lt;h3 id=&quot;方法三使用-drawingcontext-绘制并配合-guidelineset&quot;&gt;方法三：使用 DrawingContext 绘制并配合 GuidelineSet&lt;/h3&gt;

&lt;p&gt;如果自己处理绘制，则可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnRender&lt;/code&gt; 方法中使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingContext&lt;/code&gt; 来绘制各种各样的形状。&lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingContext&lt;/code&gt; 有方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushGuidelineSet&lt;/code&gt;，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;PushGuidelineSet&lt;/code&gt; 就是用来处理对齐的。&lt;/p&gt;

&lt;p&gt;以下是四种不同方式的对齐效果对比，其中上面一半是直接对齐（即绘制过程是紧贴着的），下面一半则是多个部分带上一点偏移（即并不是紧贴）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-20-50-44.png&quot; alt=&quot;四种方式对比&quot; /&gt;&lt;br /&gt;
▲ 看不清的可以考虑方法看&lt;/p&gt;

&lt;p&gt;于是要想像素对齐，必须：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;布局或绘制时，UI 元素之间一点偏移或空隙都不能有，一点都不行&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SnapsToDevicePixels&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;GuidelineSet&lt;/code&gt; 在实际对齐中有效，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseLayoutRounding&lt;/code&gt; 就是在逗你&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GuidelineSet&lt;/code&gt; 的使用可以参考我在 StackOverflow 上的回答：&lt;a href=&quot;https://stackoverflow.com/a/45189552/6233938&quot;&gt;graphics - WPF DrawingContext seems ignore SnapToDevicePixels - Stack Overflow&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;以下是我编写的用于辅助绘制对齐线条的扩展方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SnapDrawingExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DrawSnappedLinesBetweenPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawingContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Pen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineThickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guidelineSet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GuidelineSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;guidelineSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GuidelinesX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;guidelineSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GuidelinesY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineThickness&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PushGuidelineSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guidelineSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意添加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;GuidelineSet&lt;/code&gt; 的尺寸&lt;strong&gt;不需要是整数，也不需要计算对齐屏幕的位置&lt;/strong&gt;，只需要&lt;strong&gt;随便指定一个值即可，但相邻的绘制元素的值需要在 double 级别完全相同，多一点少一点都不行&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;另外还需要特别注意的是：如果你绘制矩形，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;GuidelineSet&lt;/code&gt; &lt;strong&gt;构造函数参数传入的是横坐标和纵坐标，不要把宽度和高度传进去了&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnRender&lt;/code&gt; 中调用它绘制：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnRender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DrawingContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Draw four horizontal lines and one vertical line.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice that even the point X or Y is not an integer, the line is still snapped to device.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DrawSnappedLinesBetweenPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LineThickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;119.7777&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;119.7777&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-21-32-09.png&quot; alt=&quot;绘制的四条线&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;方法四renderoptionsedgemode&quot;&gt;方法四：RenderOptions.EdgeMode&lt;/h3&gt;

&lt;p&gt;这是纯渲染级别的附加属性，对所有 UI 元素有效。这个属性很神奇，一旦设置，元素就再也不会出现模糊的边缘了，一定是硬像素边缘。不足半像素的全部删掉，超过半像素的变为 1 个像素。&lt;/p&gt;

&lt;p&gt;以为它可以解决问题？——&lt;strong&gt;Too young, too simple.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;你希望能够绘制 1 像素的线条，实际上它会让你有时看得见 1 像素线条，有时看的是 2 像素线条，有时居然完全看不见！！！&lt;/p&gt;

&lt;p&gt;如果你都作用对象上还有其它视觉对象，它们也会一并变成了“硬边缘”，是可以看得见一个个像素的边缘。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-22-09-53.png&quot; alt=&quot;硬边缘&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;各种方法适用范围总结&quot;&gt;各种方法适用范围总结&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-12-21-39-04.png&quot; alt=&quot;适用范围总结&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果画粗线条粗边框，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderOptions.EdgeMode&lt;/code&gt; 最适合了，因为设置起来最方便，可以设置到所有的 UI 元素上。由于边框很粗，所以多一个少一个像素用户也注意不到。&lt;/li&gt;
  &lt;li&gt;如果是画细边框，那么使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 配合 &lt;code class=&quot;highlighter-rouge&quot;&gt;SnapsToDevicePixels&lt;/code&gt; 可以解决，无论是 0.8 像素还是 1.0 像素，1.2 像素，都能在准确地显示其粗细的基础之上还保证像素对齐。&lt;/li&gt;
  &lt;li&gt;如果图形比较复杂，比如绘制表格或者其它各种交叉了线条的图形，那么使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DrawingContext&lt;/code&gt; 绘制，并设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;GuidelineSet&lt;/code&gt; 对齐。&lt;/li&gt;
  &lt;li&gt;如果窗口非常简单，既没有缩放，UI 元素也不多，可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;UseLayoutRounding&lt;/code&gt; 碰碰运气，万一界面简单到只需要整数对齐就够了呢？&lt;/li&gt;
  &lt;li&gt;特别说明，上面四种方法不足与应对所有的像素对齐情况，如果还是没办法对齐……节哀把……我们一起找偏方……&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Mon, 13 Aug 2018 12:47:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/draw-aligned-lines-using-guidelineset.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/draw-aligned-lines-using-guidelineset.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>C#/.NET 序列化和反序列化 YAML 元数据</title>
        <description>&lt;p&gt;我希望能够对我博客中的所有 YAML 元数据进行格式化和自动生成，于是我需要进行一些 YAML 解析和写入的操作。&lt;/p&gt;

&lt;p&gt;.NET 并没有原生提供对 YAML 的序列化和反序列化。虽然 YAML 文件的解析并不难，不过如果不是处于特别的理由（比如性能），使用现有的库解析 YAML 是比较好的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文推荐使用 &lt;a href=&quot;https://www.nuget.org/packages/YamlDotNet/&quot;&gt;YamlDotNet&lt;/a&gt; 序列化和反序列化 YAML。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;yaml-元数据&quot;&gt;YAML 元数据&lt;/h2&gt;

&lt;p&gt;作为示例，我拿出我在去年写的一篇博客的元数据进行分析：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;post&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;利用&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TypeConverter，转换字符串和各种类型只需写一个函数&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;date_published&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2017-01-17 18:13:00 +0800&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2018-04-23 07:31:32 +0800&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/dotnet/2017/01/17/convert-using-type-converter.html&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;keywords&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet typeconverter&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;使用 TypeConverter 实现字符串转各种类型。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，实际上元数据是包含开始标签和结束标签的。yaml 元数据以 &lt;code class=&quot;highlighter-rouge&quot;&gt;---&lt;/code&gt; 包裹，toml 元数据以 &lt;code class=&quot;highlighter-rouge&quot;&gt;+++&lt;/code&gt; 包裹。&lt;/p&gt;

&lt;p&gt;由于从 Markdown 中解析出 YAML 元数据不是本文的重点，所以我放到最后一起说明。&lt;/p&gt;

&lt;h2 id=&quot;定义-net-类型&quot;&gt;定义 .NET 类型&lt;/h2&gt;

&lt;p&gt;我们需要先定义 .NET 类型，以便 YamlDotNet 进行序列化和反序列化。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;YamlFrontMeta&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;date_published&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PublishDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;layout&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Layout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;permalink&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PermanentUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;categories&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Categories&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;keywords&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Keywords&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;versions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VersionsInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Versions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;published&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsPublished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sitemap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsInSiteMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;.NET 类型中的属性必须是 YAML 文件中属性的超集。&lt;/p&gt;

&lt;p&gt;以上 &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplyNamingConventions&lt;/code&gt; 属性的默认值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;，这为了解决一些命名约束上的问题，详见：&lt;a href=&quot;https://github.com/aaubry/YamlDotNet/issues/228&quot;&gt;YamlMember Alias isn’t applied when using the CamelCaseNamingConvention · Issue #228 · aaubry/YamlDotNet&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外，如果 YAML 属性中包含数组，则需要将属性的类型设置为集合类型。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Good&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Rely&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Good&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;——&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Six&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Design&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Principles&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;date_published&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2018-06-30 19:09:53 +0800&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2018-08-12 16:04:26 +0800&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet framework&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;中文&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/post/framework-api-design.html&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VersionInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;current&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VersionsInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;中文&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;YamlMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;English&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyNamingConventions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;English&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;序列化与反序列化&quot;&gt;序列化与反序列化&lt;/h2&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Deserializer&lt;/code&gt; 类型可以反序列化一个 YAML 元数据。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deserializer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deserializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deserializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YamlFrontMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yamlText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Serializer&lt;/code&gt; 类型可以序列化一个 YAML 元数据到字符串。这样，就能更新博客的 YAML 元数据部分了。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Serializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yamlText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 21:47:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/serialize-and-deserialize-yaml.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/serialize-and-deserialize-yaml.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 读取或修改文件的创建时间和修改时间</title>
        <description>&lt;p&gt;手工在博客中添加 Front Matter 文件头可是个相当费事儿的做法，这种事情就应该自动完成。&lt;/p&gt;

&lt;p&gt;.NET 中提供了非常方便的修改文件创建时间的方法，使用这种方法，能够帮助自动完成一部分文件头的编写或者更新。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;相关类型&quot;&gt;相关类型&lt;/h2&gt;

&lt;p&gt;.NET 中提供了两个不同的设置创建和修改时间的入口：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;File&lt;/code&gt; 静态类&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo&lt;/code&gt; 类&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-12-19-29-03.png&quot; alt=&quot;File 类的时间方法&quot; /&gt;&lt;br /&gt;
▲ File 静态类的方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-12-19-30-17.png&quot; alt=&quot;FileInfo 类的时间方法&quot; /&gt;&lt;br /&gt;
▲ FileInfo 类的方法&lt;/p&gt;

&lt;p&gt;很明显，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo&lt;/code&gt; 类可以使用属性直接获取和赋值，用法上会比 &lt;code class=&quot;highlighter-rouge&quot;&gt;File&lt;/code&gt; 方便，不过需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileInfo&lt;/code&gt; 的实例。&lt;/p&gt;

&lt;h2 id=&quot;修改时间&quot;&gt;修改时间&lt;/h2&gt;

&lt;p&gt;我期待能够读取文件的创建和修改时间来获知博客文章的发布和修改时间。不过在此之前，我需要先根据 Markdown 文件元数据更新文件时间。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixFileDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeOffset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createdTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeOffset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modifiedTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 更改文件的创建时间。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationTimeUtc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createdTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 更改文件的更新时间。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWriteTimeUtc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modifiedTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 更改文件最近一次访问的时间。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastAccessTimeUtc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至于如何获取 Markdown 文件元数据中的时间，可以使用 &lt;a href=&quot;https://www.nuget.org/packages/YamlDotNet/&quot;&gt;YamlDotNet&lt;/a&gt;（当然，需要自己提取 YAML 元数据头）。&lt;/p&gt;

&lt;h2 id=&quot;读取时间&quot;&gt;读取时间&lt;/h2&gt;

&lt;p&gt;当此后需要使用文件的创建时间来更新 YAML 元数据时，只需要读取这几个属性即可。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;UpdateMetaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationTimeUtc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWriteTimeUtc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateMetaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeOffset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publishDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeOffset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publishDateString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLocalTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yyyy-MM-dd HH:mm:ss zz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLocalTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yyyy-MM-dd HH:mm:ss zz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 省略更新 YAML 元数据。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;关于-utc-时间&quot;&gt;关于 UTC 时间&lt;/h2&gt;

&lt;p&gt;也许你注意到以上我使用的时间类型都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DateTimeOffset&lt;/code&gt; 而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DateTime&lt;/code&gt;，这是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;DateTimeOffset&lt;/code&gt; 中记录了时区信息，不至于在使用的过程中丢掉时区信息，出现重复时间转换，发生时间错误。&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 11:44:24 +0000</pubDate>
        <link>https://blog.walterlv.com/post/read-write-file-date-attributes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/read-write-file-date-attributes.html</guid>
        
        
        <category>windows</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Good Framework Rely on Good Api —— Six API Design Principles</title>
        <description>&lt;p&gt;We have &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot;&gt;S.O.L.I.D&lt;/a&gt; principles of object-oriented programming, and we also have &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_design_pattern&quot;&gt;Software design patterns&lt;/a&gt; to solve general, reusable solution to a commonly occurring problem. But we don’t have public-accepted API design principles or patterns for us to develop better APIs.&lt;/p&gt;

&lt;p&gt;But we still have many API designing experiences to conclude some design principals. This post concludes them.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/framework-api-design.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/framework-api-design-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;The API design principles in this post mostly come from &lt;em&gt;Practical API Design&lt;/em&gt; written by Jaroslav Tulach who is the NetBeans founder. I’ve read the whole book but find that most knowledge is his recommendations and are scattered to be in order. So I collect the core API design recommendations into six design principles. Maybe this post will help you to evaluate your Framework and API design quality and help you to write good APIs that gives the user better experience.&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-is-api&quot;&gt;What is API?&lt;/h2&gt;

&lt;p&gt;API is the short of Application programming interface. Wikipedia has a definition for it, but it’s a bit hard to understand. See this link to view the definition: &lt;a href=&quot;https://en.wikipedia.org/wiki/Application_programming_interface&quot;&gt;Application programming interface - Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can simply treat &lt;code class=&quot;highlighter-rouge&quot;&gt;class&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;interface&lt;/code&gt;, Property, Field, Method, and the configuration file or the protocol provided by the library as APIs.&lt;/p&gt;

&lt;h2 id=&quot;api-design-principles&quot;&gt;API design principles&lt;/h2&gt;

&lt;p&gt;Even if you don’t learn anything nor read any books about API design, if you have programmed a few times long, you’ll feel that some APIs are easier to use and others are not. This means that every programmer has more or less API usage experience.&lt;/p&gt;

&lt;p&gt;So the principals concluded in this post will help us design better API for our library users.&lt;/p&gt;

&lt;h3 id=&quot;easier-to-understand&quot;&gt;Easier to understand&lt;/h3&gt;

&lt;p&gt;Some users want to use a new API and find that they must learn some new knowledge about it to write correct code. The more the user should learn new knowledge the harder the API to understand.&lt;/p&gt;

&lt;p&gt;We can follow these tips to help us design easier-to-understand APIs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Don’t introduce new concept if not necessary.&lt;/li&gt;
  &lt;li&gt;Prevent the user to use it incorrectly.
    &lt;ul&gt;
      &lt;li&gt;Make the user cannot write incorrect code. It means you can make the code uncompilable when the user uses it incorrectly.&lt;/li&gt;
      &lt;li&gt;If you find that is hard to make the wrong code uncompilable, you can throw exceptions to warn the user and provide tips for him/her to fix it.&lt;/li&gt;
      &lt;li&gt;You’d better make wrong code ugly at the same time. e.g. Make the IDE display underlines in the wrong code.&lt;/li&gt;
      &lt;li&gt;If you can only warn the user in your documentation, you may tried less. It’s recommended to try more to do the things above.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Moq in .NET foundation is a very good practice for &lt;em&gt;Prevent the user to use it incorrectly&lt;/em&gt;. You can install and try it in &lt;a href=&quot;https://www.nuget.org/packages/Moq/&quot;&gt;Moq in nuget.org&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;easier-to-find&quot;&gt;Easier to find&lt;/h3&gt;

&lt;p&gt;Most of us use IDE to develop and maybe some of us use code editor such as &lt;em&gt;Visual Studio Code&lt;/em&gt;, &lt;em&gt;Sublime&lt;/em&gt;, &lt;em&gt;Atom&lt;/em&gt;, &lt;em&gt;Notepad++&lt;/em&gt; or &lt;em&gt;Vim&lt;/em&gt;. Whatever you use to write code, they all have IntelliSense which can help you know more context APIs and write correct API usage code.&lt;/p&gt;

&lt;p&gt;If we can find some new API and use it correctly via IntelliSense, we can say that the API is easier to find.&lt;/p&gt;

&lt;p&gt;How do we find the APIs via IntelliSense?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When we implement a method which we don’t know anything about its parameters, we can get right values we want from the arguments via the IntelliSense.&lt;/li&gt;
  &lt;li&gt;When we call a method which we don’t know anything about its parameters, we can fetch and create what it needs via the IntelliSense.&lt;/li&gt;
  &lt;li&gt;When we call a method whose return value type is unfamiliar for us, we can use the return value correctly via the IntelliSense.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a picture below I draw to describe APIs easier or harder to find.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-30-15-59-13.png&quot; alt=&quot;Easier/Harder to Find&quot; /&gt;&lt;br /&gt;
▲ The connection lines indicates that we can know the APIs through the method parameters and the returning value.&lt;/p&gt;

&lt;h3 id=&quot;related-apis-are-more-similar&quot;&gt;Related APIs are more similar&lt;/h3&gt;

&lt;p&gt;If the similar functions have similar APIs, the API users cost very less to learn the correct usage of the new API.&lt;/p&gt;

&lt;p&gt;You may remember the &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt; method of LINQ. And when you use LINQ to XML to read/write XML files, you’ll find &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt; method, too. Their usage experiences are very similar so that you can easily know how to use &lt;code class=&quot;highlighter-rouge&quot;&gt;Select&lt;/code&gt; in LINQ to XML if you know the LINQ.&lt;/p&gt;

&lt;h3 id=&quot;simple-task-have-simple-implementation&quot;&gt;Simple task have simple implementation&lt;/h3&gt;

&lt;p&gt;If you only design your APIs in the recommendation of the three principles above, your classes may be too large so that they may violate the &lt;em&gt;Single Responsibility Principle&lt;/em&gt; of S.O.L.I.D. So there is another principle to prevent this being happen. That is, a simple task should have a simple implementation.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InkCanvas&lt;/code&gt; of UWP is a good practice of this principal. You can use an &lt;code class=&quot;highlighter-rouge&quot;&gt;InkCanvas&lt;/code&gt; To accept inks by writing only a simple line:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;InkCanvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inkCanvas&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can write more advanced functions by writing more customization code, but all of them are not necessary:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// The code below is from https://docs.microsoft.com/en-us/windows/uwp/design/input/pen-and-stylus-interactions&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Set supported inking device types.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inkCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InkPresenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputDeviceTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreInputDeviceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoreInputDeviceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Set initial ink stroke attributes.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;InkDrawingAttributes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InkDrawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnorePressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FitToCurve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inkCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InkPresenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateDefaultDrawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawingAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;easier-to-test-and-to-be-tested&quot;&gt;Easier to test and to be tested&lt;/h3&gt;

&lt;p&gt;Better API helps the API user easier to test his/her API usage methods.&lt;/p&gt;

&lt;p&gt;If you provide an API with a static method such as &lt;code class=&quot;highlighter-rouge&quot;&gt;Config.Get(&quot;SomeKey&quot;)&lt;/code&gt; to retrieve configuration values, the API user will find it hard to write unit test method because he/she cannot create fake configuration.&lt;/p&gt;

&lt;h3 id=&quot;easier-to-keep-compatibility-even-if-upgrading-frequently&quot;&gt;Easier to keep compatibility even if upgrading frequently&lt;/h3&gt;

&lt;p&gt;Better APIs cost less for the users to upgrade their library versions and bring less burden for the API developers to make library compatible.&lt;/p&gt;

&lt;p&gt;There are three kind of compatibility:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Binary compatibility&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;When upgrading the library, the users can run their projects without a recompile.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Code compatibility&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;When upgrading the library, the users can recompile their project without any code modification.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Behavior compatibility&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;When upgrading the library, the users’ applications run the same as before.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can follow these tips to help us design better-future-compatibility APIs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Don’t release your APIs ahead of your whole solution.
    &lt;ul&gt;
      &lt;li&gt;If you are trying to release an API for the future usage, I recommend you not to do this. Because you don’t know the future needs, the APIs have a very high probability to be changed with a heavy compatibility burden.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Reserve enough extension points.
    &lt;ul&gt;
      &lt;li&gt;If an API which will have more chance to change in the future has fewer extension points, the API will change more frequently. But if you reserve some designed extension points, future change will be in the control.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Give tips for the users to migrate legacy APIs.
    &lt;ul&gt;
      &lt;li&gt;If an API is obsolete, you’d better not to delete it immediately. It’s recommended to mark it obsolete and tell the users how to migrate to the new APIs.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;framework-design&quot;&gt;Framework design&lt;/h2&gt;

&lt;p&gt;The framework can be understood as a set of APIs developed for the complete solution of a certain kind of problems.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia&quot;&gt;Avalonia&lt;/a&gt; is a cross-platform UI framework and it uses &lt;a href=&quot;https://github.com/reactiveui/ReactiveUI&quot;&gt;ReactiveUI&lt;/a&gt; which is a reactive UI framework to develop MVVM pattern UI.&lt;/p&gt;

&lt;p&gt;Hope you’ll design better APIs by reading the six API design principals.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Practical API Design&lt;/em&gt; by Jaroslav Tulach (The NetBeans founder)&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:04:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/framework-api-design-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/framework-api-design-en.html</guid>
        
        
        <category>dotnet</category>
        
        <category>framework</category>
        
      </item>
    
      <item>
        <title>WPF/UWP 的 Grid 布局竟然有 Bug，还不止一个！了解 Grid 中那些未定义的布局规则</title>
        <description>&lt;p&gt;只要你用 XAML 写代码，我敢打赌你一定用各种方式使(nuè)用(dài)过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;。不知你有没有在此过程中看到过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 那些匪夷所思的布局结果呢？&lt;/p&gt;

&lt;p&gt;本文将带你来看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局中的 Bug。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/the-bugs-of-grid.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/the-bugs-of-grid-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;无限空间下的比例&quot;&gt;无限空间下的比例&lt;/h2&gt;

&lt;p&gt;先上一段代码，直接复制到你的试验项目中运行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一列固定 &lt;code class=&quot;highlighter-rouge&quot;&gt;100&lt;/code&gt;，第二列占 1 个比例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;，第三列占 2 个比例的 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;。你觉得最终的效果中，第二个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 和第三个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 的可见尺寸分别是多少呢？&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;按&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;下&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;F5&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;运&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;行&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;看&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;看&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;结&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;果&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-15-13.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;预料到了吗？虽然第二列和第三列的比例是 1:2，但最终的可见比例却是 1:1。&lt;/p&gt;

&lt;p&gt;这里是有破绽的，因为你可能会怀疑第三列其实已经是第二列的两倍，只是右侧是空白，看不出来。那么现在，我们去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;Canvas&lt;/code&gt;，改用在父 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 中右对齐，也就是如下代码：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行后，你会发现最右侧是没有空白的，也就是说第二列和第三列确实不存在 1:2 的比例——它们是等宽的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-33-09.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么那一段失去的空间去哪里了呢？让我们缩小窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-space-move.gif&quot; alt=&quot;缩小窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;竟然在左侧还有剩余空间的情况下，右侧就开始压缩元素空间了！我们能说那段丢失的一个 * 长度的空白到左边去了吗？显然不能。&lt;/p&gt;

&lt;p&gt;不过，我们能够猜测，压缩右侧元素开始于最小 1:2 的比例正好不足时出现。&lt;/p&gt;

&lt;h2 id=&quot;刚好不够分的比例&quot;&gt;刚好不够分的比例&lt;/h2&gt;

&lt;p&gt;右对齐能够帮助我们区分右侧是否真的占有空间。那么我们继续右对齐做试验。&lt;/p&gt;

&lt;p&gt;现在，我们将第二列的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 做成跨第二和第三两列的元素。第三列的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 放到第二列中。（也就是说，我们第三列不放元素了。）&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行看看，在得知前一节现象的情况下，新的现象并没有出现多大的意外。第三列凭空消失，第二列与之之间依然失去了 1:2 的比例关系。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-43-04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，我们还可以缩小窗口。&lt;/p&gt;

&lt;p&gt;缩&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;小&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;窗&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;口&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;后&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;竟&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;然&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-space-appear.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为什么在缩小窗口的时候突然间出现了那个红色的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;？为什么在红色 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 的右边还留有空白？&lt;/p&gt;

&lt;p&gt;如果说第一节中我们认识到右对齐时右边剩余的空白空间会丢掉，那么为什么此时右边剩余的空白空间会突然出现？&lt;/p&gt;

&lt;p&gt;我试着稍微增加第二个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 的宽度，突然间，刚刚缩小窗口时的行为也能复现！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-a-little-longer.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;自动尺寸也能玩比例&quot;&gt;自动尺寸也能玩比例&lt;/h2&gt;

&lt;p&gt;现在，我们抛弃之前的右对齐测试方法，也不再使用预期按比例划分空间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;。我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; 来实现比例功能。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFF6347&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F008080&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体说来，我们有四个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 了，放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; 尺寸的三列中。第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 横跨三列，尺寸比其他总和都长，达到了 159；剩下的三个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 各占一列，其中两边等长，中间稍长。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-02-21.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么实际布局中各列是怎么分的呢？以下是设计器为我们显示的列宽：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-04-07.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;46&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;69&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;46&lt;/code&gt; 是怎么来的？莫非是 &lt;code class=&quot;highlighter-rouge&quot;&gt;46:69&lt;/code&gt; 与 &lt;code class=&quot;highlighter-rouge&quot;&gt;28:51&lt;/code&gt; 相同？然而实际计算结果却并不是！&lt;/p&gt;

&lt;p&gt;可万一这是计算误差呢？&lt;/p&gt;

&lt;p&gt;那么我们再来看看三个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 的另外两组值：&lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-08-41.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ &lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-09-29.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt; 最终得到的是相同比例，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt; 得到的列宽比例与 &lt;code class=&quot;highlighter-rouge&quot;&gt;1:2&lt;/code&gt; 相去甚远。也就是说，其实 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 内部并没有按照元素所需的尺寸来按比例计算列宽。&lt;/p&gt;

&lt;h2 id=&quot;相同比例也能有不同尺寸&quot;&gt;相同比例也能有不同尺寸&lt;/h2&gt;

&lt;p&gt;在上一节的试验中，不管比例如何，至少相同的设置尺寸带来了相同的最终可见尺寸。然而，就算是这一点，也是能被颠覆的。&lt;/p&gt;

&lt;p&gt;现在，我们将 3 列换成 4 列，&lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 数量换成 6 个。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFF6347&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F008080&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体来说，第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 跨前三列，第二个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 跨后三列，跟前一节的长 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 一样长。第三和第六个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 分在两边，与之前的短 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 一样短。中间的两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 与之前中间的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 一样长。就像下图所示的这样。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-18-14.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么此时布局出来的列宽是多少呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-21-03.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 32:65:65:39&lt;/p&gt;

&lt;p&gt;等等！那个 39 是怎么来的？如果前一节里相等尺寸的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; 会得到相等尺寸的列宽，那么这里也将颠覆！事实上，即便此时列宽比例与元素所需比例一致，在这种布局下也是有无穷多个解的。WPF 只是从这无穷多个解中挑选了一个出来——而且，还无法解释！&lt;/p&gt;

&lt;h2 id=&quot;总结-grid-未定义的规则&quot;&gt;总结 Grid 未定义的规则&lt;/h2&gt;

&lt;p&gt;总而言之，言而总之，&lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局在特殊情况下是有一些不合常理的。我称之为“未定义的规则”。这些未定义的规则总结起来有以下三点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在无穷大布局空间时的 * 的比例&lt;/li&gt;
  &lt;li&gt;在跨多列布局时 * 的比例&lt;/li&gt;
  &lt;li&gt;在全 Auto 尺寸时各列尺寸&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过你也可能会吐槽我的用法不对，可是，作为一个连表现行为都公开的 API，其行为也是 API 的一部分，应该具有明确可追溯可文档化的行为；而不是由用户去探索，最终无法猜测可发生事情的行为。&lt;/p&gt;

&lt;p&gt;微软没有任何官方文档公开了这些诡异的行为，我也没有在任何第三方资料中找到这样的行为（这些都是我自己总结的）。我认为，微软没有为此公开文档是因为行为太过诡异，无法编写成文档！&lt;/p&gt;

&lt;p&gt;你可能还会质疑，可以去 &lt;a href=&quot;https://referencesource.microsoft.com/&quot;&gt;Reference Source&lt;/a&gt; 查阅 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局的源码，那样就能解释这些诡异的行为了。确实如此，那里是这一切诡异布局背后的罪魁祸首。&lt;/p&gt;

&lt;p&gt;我阅读过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的布局源码，但没能全部理解，而且在阅读的过程中发现了一些微软官方承认的 Bug（我也没有能力去解决它）。&lt;/p&gt;

&lt;p&gt;不过，我整整三天的时间写了一个全新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局算法（&lt;em&gt;感谢 @&lt;a href=&quot;https://lindexi.github.io/lindexi/&quot;&gt;林德熙&lt;/a&gt; 抽出时间跟我探讨 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的布局算法&lt;/em&gt;）。在新的算法中，对于微软公开的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局行为，我跟它的表现是一样的。对于本文中提到的各种 Bug，我找不到手段实现跟它一模一样的布局结果，但是，我可以文档化地完全确定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 整个布局的所有行为。包括以上所有我认为的“未定义的规则”。&lt;/p&gt;

&lt;p&gt;新 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局算法的源码在 GitHub 上，我提交给了 Avalonia：&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia/pull/1517/files&quot;&gt;A new grid layout algorithm to improve performance and fix some bugs by walterlv · Pull Request #1517 · AvaloniaUI/Avalonia&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:04:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-bugs-of-grid.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-bugs-of-grid.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>The undefined behaviors of WPF Grid (the so-called bugs)</title>
        <description>&lt;p&gt;As long as you write code in XAML, I bet you must have used &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; in various ways. I wonder if you have seen any strange layout results of &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I’ll talk about the undefined behaviors of &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout in this post. I call them the bugs.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is written in &lt;strong&gt;multiple languages&lt;/strong&gt;. Please select yours:&lt;/p&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/the-bugs-of-grid.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/the-bugs-of-grid-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reading Tips:&lt;/strong&gt; All of the examples described in this article are not common usages for &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;. (Microsoft is a great company. It will never do strange things on common situation.)&lt;/p&gt;

&lt;h2 id=&quot;star-unit-on-infinite-space&quot;&gt;Star Unit on Infinite space&lt;/h2&gt;

&lt;p&gt;Copy and paste the code below and run to view the result:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The 1st column is &lt;code class=&quot;highlighter-rouge&quot;&gt;100&lt;/code&gt;-pixel fixed-width. The 2nd column is &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;, and the 3rd one is &lt;code class=&quot;highlighter-rouge&quot;&gt;2*&lt;/code&gt;. Then what’s the visible width of the 2nd &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; and the 3rd &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Press&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;F5&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;in&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Visual&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Studio&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;view&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;the&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;result&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-15-13.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Did you predicate the result? Although the 2nd and the 3rd column width proportion is 1:2, the final visible proportion is 1:1.&lt;/p&gt;

&lt;p&gt;There are flaws here, because you may suspect that the 3rd column is already twice as much as the 2nd column, but the right side is blank and cannot be seen. So now, we remove the &lt;code class=&quot;highlighter-rouge&quot;&gt;Canvas&lt;/code&gt; and use &lt;code class=&quot;highlighter-rouge&quot;&gt;HorizontalAlignment=&quot;Right&quot;&lt;/code&gt;. The new code is shown below:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running, you will find that there is no white space on the far right, that is to say, the 2nd and 3rd columns do not have a 1:2 ratio - they are equal.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-33-09.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So where is the lost space? Let’s resize the window to check it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-space-move.gif&quot; alt=&quot;narrow the window&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Even if there is space left on the left, the right side begins to clip the element space! Can we say that the length of a missing * length has gone to the left? Obviously not. However, we can guess that the clipping of the right side of the element begins at the 1:2 ratio.&lt;/p&gt;

&lt;h2 id=&quot;star-unit-at-the-size-just-required&quot;&gt;Star Unit at the Size Just Required&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HorizontalAlignment=&quot;True&quot;&lt;/code&gt; helps us a lot to distinguish whether the right side really occupies space. So we continue the testing on the right-alignment.&lt;/p&gt;

&lt;p&gt;Now, we modify the 2nd column &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; to span the 2nd and 3rd columns. The 3rd column &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; is placed into the 2nd column. (In other words, our 3rd column does not contain any &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;.)&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CornflowerBlue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tomato&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The new behavior did not show much surprise to us because we have seen the behavior last section. The 3rd column disappeared, and the 2nd column still lost the 1:2 ratio.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-14-43-04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Narrow the window again.&lt;/p&gt;

&lt;p&gt;Narrow&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;the window&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;again&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;view&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;the behavior&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-space-appear.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Why did the tomato &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; suddenly appear when the window was narrowing? Why is there a blank space on the right side of the tomato &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;If we have realized in the last post section that the right-side space is lost when it is right-aligned, why does the white space appear suddenly in the right-side again?&lt;/p&gt;

&lt;p&gt;I tried to slightly increase the width of the second &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;. Suddenly, I reproduced the strange behavior that I reproduced just now when resizing the window!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-a-little-longer.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-proportion-of-auto-size&quot;&gt;The Proportion of Auto Size&lt;/h2&gt;

&lt;p&gt;Now, abandon the previous right-aligned test method and no longer use the &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; width to separate the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;. We use &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; instead.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFF6347&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F008080&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Specifically, we have four &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;, placed in three columns of &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; size. The first &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; spans three columns, and its size is longer than all the others, reaching 159. The remaining three &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; each occupy a column, with two sides of equal length and a slightly longer middle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-02-21.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;How are the columns in the actual layout divided? Here is the column width that the designer shows for us:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-04-07.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Where do &lt;code class=&quot;highlighter-rouge&quot;&gt;46&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;69&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;46&lt;/code&gt; come from? Could it be that the proportion of &lt;code class=&quot;highlighter-rouge&quot;&gt;46:69&lt;/code&gt; is the same as that of &lt;code class=&quot;highlighter-rouge&quot;&gt;28:51&lt;/code&gt;? However, the actual calculation result is not!&lt;/p&gt;

&lt;p&gt;What if this is a calculation error?&lt;/p&gt;

&lt;p&gt;So let’s look at the other two sets of values ​​for the three &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;: &lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-08-41.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ &lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-09-29.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;highlighter-rouge&quot;&gt;50:50:50&lt;/code&gt;, we eventually get the 1:1:1 proportion. But the ratio of column widths in &lt;code class=&quot;highlighter-rouge&quot;&gt;25:50:25&lt;/code&gt; is far from &lt;code class=&quot;highlighter-rouge&quot;&gt;1:2:1&lt;/code&gt;. That is, in fact, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; does not calculate the column widths by the proportion to the size of the element.&lt;/p&gt;

&lt;h2 id=&quot;the-same-element-size-but-different-column-width&quot;&gt;The same Element Size but Different Column Width&lt;/h2&gt;

&lt;p&gt;In the experiment in the previous section, we notice that the same size brought about the same final visible size regardless of the proportion. However, this conclusion still can be subverted.&lt;/p&gt;

&lt;p&gt;Now, we will replace 3 columns with 4 columns, and the number of &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; will be replaced with 6.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;159&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PaleGreen&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFF6347&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FC71585&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7F008080&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Specifically, the first &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; spans the first three columns, and the second &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; spans the last three columns, the same length as the long &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; of the previous section. The third and sixth &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;s are on two sides and are as short as the previous &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;. The middle two &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;s are as long as the previous &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt;. Just like the picture that is shown below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-18-14.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What is the width of the columns laid out at this time?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-05-15-21-03.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 32:65:65:39&lt;/p&gt;

&lt;p&gt;Wait! Where did the 39 come from? If the equal-size &lt;code class=&quot;highlighter-rouge&quot;&gt;Border&lt;/code&gt; in the previous section would get equal-sized column widths, then this will also subvert! In fact, even if the proportion of the column width to the proportion of elements is the same at this time, there are as infinitely as many solutions under this layout. WPF picks only one out of this infinite number of solutions - and it cannot explain itself!&lt;/p&gt;

&lt;h2 id=&quot;the-conclusion-of-the-grid-undefined-behavior&quot;&gt;The conclusion of the Grid undefined behavior&lt;/h2&gt;

&lt;p&gt;In summary, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout has some unreasonable behaviors under special circumstances. I call them “the undefined behaviors”. These undefined behaviors are summarized in the following three points:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Infinite layout space with * unit size&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; unit column/row with multiple-span elements&lt;/li&gt;
  &lt;li&gt;Auto size in all column/row&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, you may think that I use the &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; in incorrect ways. However, as an API that exposes the behaviors, the behavior itself is also a part of the API. It should have clear traceable and documentable behavior instead of being explored and guess and failed by the user.&lt;/p&gt;

&lt;p&gt;Microsoft does not have any official documents that disclose these bizarre behaviors, and I have not found such behavior in any third-party references (this post is my own conclusion). I think that Microsoft did not publish this kind of documents because the behaviors are too bizarre to be documented!&lt;/p&gt;

&lt;p&gt;You may also be skeptical that I can go to &lt;a href=&quot;https://referencesource.microsoft.com/&quot;&gt;Reference Source&lt;/a&gt; to check the source code of the `Grid’ layout, and then I can explain these strange behaviors. Indeed, the code there is the culprit behind this all-odd layout.&lt;/p&gt;

&lt;p&gt;I have read the layout source code of &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt;, and I can’t understand all the logic of its layout algorithm, and I also have found out some official Microsoft-recognized bugs in my reading (But I have no ability to solve it).&lt;/p&gt;

&lt;p&gt;However, I have written a totally new &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout algorithm (&lt;em&gt;thanks to @&lt;a href=&quot;https://lindexi.github.io/lindexi/&quot;&gt;lindexi&lt;/a&gt; that he spend so much time on discussing the layout algorithm with me.&lt;/em&gt;). In the new algorithm, I do the same with Microsoft’s public documented &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout behavior. But for the various bugs mentioned in this post, I can’t find a way to achieve the same layout results as it does, but I can document and determine all the behaviors of the new &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout, without any undefined behavior.&lt;/p&gt;

&lt;p&gt;I’ve created a new pull request for the new &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; layout algorithm to Avalonia. Goto &lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia/pull/1517/files&quot;&gt;A new grid layout algorithm to improve performance and fix some bugs by walterlv · Pull Request #1517 · AvaloniaUI/Avalonia&lt;/a&gt; to view the source code and to see more details.&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:04:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-bugs-of-grid-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-bugs-of-grid-en.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>xaml</category>
        
      </item>
    
      <item>
        <title>Introducing MSTestEnhancer to make unit test result easy to read</title>
        <description>&lt;p&gt;Don’t you think that naming is very very hard? Especially naming for unit test method? Read this article for more data of naming: &lt;a href=&quot;https://www.itworld.com/article/2833265/cloud-computing/don-t-go-into-programming-if-you-don-t-have-a-good-thesaurus.html&quot;&gt;Don’t go into programming if you don’t have a good thesaurus - ITworld&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;MSTestEnhancer is a contract-style unit test extension for MSTestv2. You can write method contract descriptions instead of writing confusing test method name when writing unit tests.&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/get-rid-or-naming-in-unit-test.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/introduce-mstest-enhancer.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;You’ll get the test result like the picture shown below:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-13-13-09-26.png&quot; alt=&quot;Test results&quot; /&gt;&lt;br /&gt;
▲ The unit test result that listed via ReSharper.&lt;/p&gt;

&lt;h2 id=&quot;classical-style-of-writing-unit-tests&quot;&gt;Classical Style of Writing Unit Tests&lt;/h2&gt;

&lt;p&gt;We used to be recommended to write unit test like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TheTestedClassTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TheTestedMethod_Condition1_Expect1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Test code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TheTestedMethod_Condition2_Expect2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Test code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is an example using MSTest. If we use NUnit or XUnit, we’ll get similar test code, too. Sometimes the conditions and expects are more complex, and we cannot write them down with only a few words. And the more complex the method name is, the more difficult for coders to read and comprehend them.&lt;/p&gt;

&lt;h2 id=&quot;introduce-mstestenhancer&quot;&gt;Introduce MSTestEnhancer&lt;/h2&gt;

&lt;p&gt;MSTestEnhancer is a MSTest v2 extension to connect unit test and the method that should be tested. You’ll find out that all unit test contracts are listed under target methods, and you can see all the result of them directly, no need to translate the obscure method name into what you want to test.&lt;/p&gt;

&lt;p&gt;Now, let’s start!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://www.nuget.org/packages/MSTestEnhancer/&quot;&gt;MSTestEnhancer&lt;/a&gt; from the &lt;a href=&quot;https://www.nuget.org/&quot;&gt;nuget.org&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Write unit test code in the style listed below.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;recommended-style-of-writing-unit-tests&quot;&gt;Recommended Style of Writing Unit Tests&lt;/h2&gt;

&lt;p&gt;Assuming that you want to test a class named &lt;code class=&quot;highlighter-rouge&quot;&gt;TheTestedClass&lt;/code&gt; containing a method named &lt;code class=&quot;highlighter-rouge&quot;&gt;TheTestedMethod&lt;/code&gt;. Then you can write unit tests like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TheTestedClassTest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContractTestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TheTestedMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;When Xxx happens, results in Yyy.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Write test case code here...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            
            &lt;span class=&quot;s&quot;&gt;&quot;When Zzz happens, results in Www.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Write test case code here...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the name of class and method are almost the name of the tested class and tested method. As a result, we don’t need to think about anything about naming unit test, nor to read the obscure name of the unit test.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-13-13-09-26.png&quot; alt=&quot;Test results&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;advanced-usages&quot;&gt;Advanced Usages&lt;/h2&gt;

&lt;h3 id=&quot;unit-test-with-arguments&quot;&gt;Unit Test with Arguments&lt;/h3&gt;

&lt;p&gt;Some unit tests need multiple values to verify the contracts, so MSTestEnhancer provides &lt;code class=&quot;highlighter-rouge&quot;&gt;WithArguments&lt;/code&gt; method to config the arguments.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;&quot;prime number.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Write test case code here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;&quot;{0} is not a prime number.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Write test case code here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can pass up to 8 parameters into the test case.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;&quot;Contract 1: {0} and {1} are allowed in the contract description.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Now, a is 2 and b is 3.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;&quot;Contract 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Now the test case will run twice. The first group, a is 2 and b is 3; and the second group, a is 10 and b is 20.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ValueTuple is supported, too.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, the contract description will be replaced to the arguments that you have passed into.&lt;/p&gt;

&lt;h3 id=&quot;async-unit-test&quot;&gt;Async Unit Test&lt;/h3&gt;

&lt;p&gt;All &lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt; extension method support async action so that you can test any async method.&lt;/p&gt;

&lt;h3 id=&quot;some-fantastic-feature&quot;&gt;Some Fantastic Feature&lt;/h3&gt;

&lt;p&gt;Nested unit test classes are supported by MSTest v2, so you can write an infinite level unit test tree.&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:03:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/introduce-mstest-enhancer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/introduce-mstest-enhancer.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>unittest</category>
        
      </item>
    
      <item>
        <title>不再为命名而苦恼！使用 MSTestEnhancer 单元测试扩展，写契约就够了</title>
        <description>&lt;p&gt;有没有觉得命名太难？有没有觉得单元测试的命名更难？没错，你不是一个人！看看这个你就知道了：&lt;a href=&quot;http://blog.jobbole.com/50708/#rd?sukey=fc78a68049a14bb285ac0d81ca56806ac10192f4946a780ea3f3dd630804f86056e6fcfe6fcaeddb3dc04830b7e3b3eb&quot;&gt;程序员最头疼的事：命名&lt;/a&gt; 或它的英文原文 &lt;a href=&quot;https://www.itworld.com/article/2833265/cloud-computing/don-t-go-into-programming-if-you-don-t-have-a-good-thesaurus.html&quot;&gt;Don’t go into programming if you don’t have a good thesaurus - ITworld&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;立刻前往 &lt;a href=&quot;https://www.nuget.org/&quot;&gt;nuget.org&lt;/a&gt; 下载安装 &lt;a href=&quot;https://www.nuget.org/packages/MSTestEnhancer/&quot;&gt;MSTestEnhancer&lt;/a&gt; 即可解决命名的苦恼。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/get-rid-or-naming-in-unit-test.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/introduce-mstest-enhancer.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;体验-mstestenhancer&quot;&gt;体验 MSTestEnhancer&lt;/h2&gt;

&lt;p&gt;看看苦恼的单元测试怎么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;被测类名&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;被测方法名&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;条件&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;预期&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;被测方法名&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;条件&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;预期&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是以 MSTest 为例，但 NUnit、XUnit 等编写体验于此也类似，都需要为测试方法命名。在这个例子中，我们写了中文的 &lt;code class=&quot;highlighter-rouge&quot;&gt;条件&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;预期&lt;/code&gt;，在实际编写时，可能是更加复杂的短句，例如：&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNull&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ThrowsArgumentNullException&lt;/code&gt;，于是最终的方法名可能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetMethod_ArgumentNull_ThrowsArgumentNullException&lt;/code&gt;。这样的方法多了也就难以读懂单元测试的代码了。&lt;/p&gt;

&lt;p&gt;然而现在看看 MSTestEnhancer 的单元测试怎么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;被测类名&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContractTestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;被测方法名&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;契约 1（当 Xxx 时，应该发生 Yyy）&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        
        &lt;span class=&quot;s&quot;&gt;&quot;契约 2（但当 Zzz 时，应该发生 Www）&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有没有觉得很直观？条件和预期直接以中文字符串的形式写在了代码里，所有契约的阅读一目了然。而且由于不需要再写条件和预期了，所以测试方法名可以与被测方法名完全一样。也就是说——再也不用为单元测试的方法取名字而伤透脑筋了。&lt;/p&gt;

&lt;p&gt;可是，工具支持呢？不要紧，在工具中也能显示中文的契约，Visual Studio 中的测试管理器和 ReSharper 测试结果页都支持显示这些中文的契约。以下是 ReSharper 的单元测试结果页视图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-12-08-54-31.png&quot; alt=&quot;单元测试结果页&quot; /&gt;&lt;/p&gt;

&lt;p&gt;每个契约按照方法名归类防止，测试结果一目了然。&lt;/p&gt;

&lt;h2 id=&quot;参数化的单元测试&quot;&gt;参数化的单元测试&lt;/h2&gt;

&lt;p&gt;有些契约需要更多的值组合来验证正确性，那么可以在契约测试用例的后面添加参数。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;&quot;质数&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;&quot;{0} 不是质数&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 测试用例代码&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以添加多个参数（最多支持 8 个）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;&quot;契约 1，参数中可以带 {0} 和 {1}。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 现在，a 会等于 2，b 会等于 3。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;&quot;契约 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 现在有两组代码，一组 a=2, b=3；另一组 a=10, b=20。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 当然也可以传入元组数组。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在显示单元测试结果时，如果契约字符串中含有格式化占位符 &lt;code class=&quot;highlighter-rouge&quot;&gt;{0}&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;{1}&lt;/code&gt; 等，会被自动替换为参数的值。&lt;/p&gt;

&lt;h2 id=&quot;异步的单元测试&quot;&gt;异步的单元测试&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt; 方法中传入的每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Action&lt;/code&gt; 都支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; 关键字，并会在执行测试用例时等待异步操作结束。&lt;/p&gt;

&lt;h2 id=&quot;额外的黑科技&quot;&gt;额外的黑科技&lt;/h2&gt;

&lt;p&gt;MSTest v2 支持嵌套类型的单元测试。也就是说，我们可以利用这一点做出近乎无限层级的单元测试树出来。&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:02:57 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-rid-or-naming-in-unit-test.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-rid-or-naming-in-unit-test.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>unittest</category>
        
      </item>
    
      <item>
        <title>使 WPF 支持触摸板的横向滚动</title>
        <description>&lt;p&gt;微软终于开始学苹果一样好好做触摸板了&lt;em&gt;（就是键盘空格键下面那一大块）&lt;/em&gt;。然而鉴于以前没有好好做，以至于 WPF 程序甚至都没有对触摸板的横向滚动提供支持&lt;em&gt;（竖向滚动是直接使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt;，汗……）&lt;/em&gt;。但有些功能真希望能够支持横向滚动！&lt;/p&gt;

&lt;p&gt;本文将介绍让触摸板支持横向滚动的方法，本质上也是用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt;，但却支持了横向。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/handle-horizontal-scrolling-of-touchpad.html&quot; selected=&quot;selected&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/handle-horizontal-scrolling-of-touchpad-en.html&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-21-19-52.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 精确式触摸板&lt;/p&gt;

&lt;p&gt;我们需要从 Windows 的窗口消息中获取 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_MOUSEHWHEEL&lt;/code&gt; 消息。对，就是鼠标滚轮消息！以前我们只取了纵向数据，现在我们要取横向数据。&lt;/p&gt;

&lt;p&gt;首先，我们需要能够监听得到消息才行。重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnSourceInitialized&lt;/code&gt; 方法可以开始监听消息；如果代码没办法写到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 中，可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.GetWindow(DependencyObject)&lt;/code&gt; 获取到窗口实例后监听它的 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceInitialized&lt;/code&gt; 事件。如果拿不到这样的时机，则只要在任何 &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceInitialized&lt;/code&gt; 之后的时机（比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Loaded&lt;/code&gt;）都可以写下面方法内部的两行代码。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里添加消息的处理。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来，我们开始处理 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_MOUSEHWHEEL&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_MOUSEHWHEEL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x020E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_MOUSEHWHEEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HIWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OnMouseTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 取指针所在高位数值。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HIWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 取指针所在低位数值。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnMouseTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里就是触摸板横向滚动的时机，参数是横向滚动的数值，就像鼠标滚轮纵向滚动的数值一样。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OnMouseTilt&lt;/code&gt; 中就可以写我们触摸板横向滚动的处理代码。&lt;/p&gt;

&lt;p&gt;以上代码都可以封装成通用的方法，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnMouseTilt&lt;/code&gt; 中抛出一个类似于 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt; 一样的事件是非常好的选择。&lt;/p&gt;

&lt;p&gt;微软的 Microsoft Sculpt Comfort Mouse 鼠标滚轮也是支持横向滚动的，以上方法也可以支持。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blogswin.blob.core.windows.net/win/sites/2/2013/05/2_5F00_77B60B43.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:02:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/handle-horizontal-scrolling-of-touchpad.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/handle-horizontal-scrolling-of-touchpad.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Support Horizontal Scrolling of TouchPad in WPF Application</title>
        <description>&lt;p&gt;Finally, Microsoft started to support touchpad like Apple did years ago. As Microsoft never do well in touchpad, WPF application even doesn’t support horizontal scrolling of touchpad. Also, WPF uses &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt; to handle vertical scrolling, not a particular method.&lt;/p&gt;

&lt;p&gt;This article contains my method to support horizontal scrolling of touchpad in a WPF application. It uses &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt; indeed, but horizontals and verticals are all supported.&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;post-inline&quot;&gt;
&lt;select onchange=&quot;self.location.href=options[selectedIndex].value&quot;&gt;
  
    
      
      
      
        &lt;option value=&quot;/post/handle-horizontal-scrolling-of-touchpad.html&quot;&gt;中文&lt;/option&gt;
      
    
  
    
      
      
      
        &lt;option value=&quot;/post/handle-horizontal-scrolling-of-touchpad-en.html&quot; selected=&quot;selected&quot;&gt;English&lt;/option&gt;
      
    
  
&lt;/select&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-21-52-22.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ Precision Touchpad&lt;/p&gt;

&lt;p&gt;We need to fetch &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_MOUSEHWHEEL&lt;/code&gt; message from our WPF window. Yes! That mouse wheel message. We fetch vertical data from it before, but we now fetch horizontal data from it.&lt;/p&gt;

&lt;p&gt;At first, we should hook the window message.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;override &lt;code class=&quot;highlighter-rouge&quot;&gt;OnSourceInitialized&lt;/code&gt; method of a &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If you could not write code in &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceInitialized&lt;/code&gt; event of a &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; is also a choice. (You can get the &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; instance by using &lt;code class=&quot;highlighter-rouge&quot;&gt;Window.GetWindow(DependencyObject)&lt;/code&gt; method.)&lt;/li&gt;
  &lt;li&gt;If you cannot get the opportune moment, you can also write code after &lt;code class=&quot;highlighter-rouge&quot;&gt;SourceInitialized&lt;/code&gt; event such as &lt;code class=&quot;highlighter-rouge&quot;&gt;Loaded&lt;/code&gt; event or others.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSourceInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PresentationSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Handle window message here.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, let’s handle &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_MOUSEHWHEEL&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_MOUSEHWHEEL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x020E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_MOUSEHWHEEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HIWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;OnMouseTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Gets high bits values of the pointer.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HIWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Gets low bits values of the pointer.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnMouseTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Write your horizontal handling codes here.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can write horizontal scrolling code in &lt;code class=&quot;highlighter-rouge&quot;&gt;OnMouseTilt&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Better yet, you could pack all the codes above in a more common class and raise a &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseTilt&lt;/code&gt; event just like raising &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseWheel&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;By the way, Microsoft Sculpt Comfort Mouse support horizontal scrolling also, and my codes above here support this kind of mouse.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blogswin.blob.core.windows.net/win/sites/2/2013/05/2_5F00_77B60B43.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/21146183/wpf-two-finger-horizontal-scrolling-on-macbook-pro-trackpad/47457389#47457389&quot;&gt;c# - WPF - Two Finger Horizontal scrolling on Macbook pro trackpad - Stack Overflow&lt;/a&gt;&lt;br /&gt;
That’s my answer!&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 08:02:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/handle-horizontal-scrolling-of-touchpad-en.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/handle-horizontal-scrolling-of-touchpad-en.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>图片点击放大，你的网页也能做到！</title>
        <description>&lt;p&gt;我经常在博客中插入大图，然而总需要借助浏览器的滚轮缩放功能放大观看实在是不方便。于是我希望做一个点击即放大的功能。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;下面就是一张可点击放大的图片，你可以点击试试！当然，我期望的效果是自动对所有博客中的图片生效。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-28-11-11-16.png&quot; alt=&quot;Fluent Design App Header&quot; /&gt;&lt;br /&gt;
▲ Fluent Design App Header&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;创建一个用于放图片的-html-节点&quot;&gt;创建一个用于放图片的 HTML 节点&lt;/h2&gt;

&lt;p&gt;如果你是普通的 HTML 网页，可以将下面的片段放入到你的页面中。&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-image&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal-content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-caption&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最外层是容器，里面包含一个关闭按钮，一张图片和一个图片标题。&lt;/p&gt;

&lt;h2 id=&quot;为图片的-html-节点添加-css-样式&quot;&gt;为图片的 HTML 节点添加 CSS 样式&lt;/h2&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.image-cover-modal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opacity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ease&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.3s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.model-shown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.image-cover-modal-content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;#image-cover-caption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.33&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;only&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;45rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;.image-cover-modal-content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加放大图片的-js-脚本&quot;&gt;添加放大图片的 JS 脚本&lt;/h2&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Get the DOM&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;image-cover-modal&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modalImg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-image&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;captionText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-caption&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// When the user clicks on &amp;lt;span&amp;gt; (x), close the modal&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;model-shown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Get the image and insert it inside the modal - use its &quot;alt&quot; text as a caption&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;model-shown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;modalImg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;captionText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;专为-jekyll-设计的简化版本&quot;&gt;专为 Jekyll 设计的简化版本&lt;/h2&gt;

&lt;p&gt;如果你使用 Jekyll 搭建静态网页，那么只需要修改 3 个地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在 main.css 中添加前面的 css 片段。&lt;/li&gt;
  &lt;li&gt;在你想要添加放大图片的页面布局（例如 post.html）中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;{% include clickable-image.html %}&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;在 _includes 文件夹中添加一个 clickable-image.html 文件，存放以下内容。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-image&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-modal-content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image-cover-caption&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Get the DOM&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;image-cover-modal&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modalImg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-image&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;captionText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-caption&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image-cover-close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// When the user clicks on &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;model-shown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Get the image and insert it inside the modal - use its &quot;alt&quot; text as a caption&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;model-shown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;modalImg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;captionText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你可以参考我的文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.github.io/blob/master/_includes/clickable-image.html&quot;&gt;/_includes/clickable-image.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.github.io/blob/eb07c3b685f94d8ce3963fb9f4a71f6346190355/_layouts/post.html#L32&quot;&gt;/_layouts/post.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/walterlv.github.io/blob/master/assets/css/main.css&quot;&gt;/assets/css/main.css at master · walterlv/walterlv.github.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3schools.com/howto/howto_css_modal_images.asp&quot;&gt;How To Create Modal Images&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:52:05 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-click-to-zoom-image-for-web-pages.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-click-to-zoom-image-for-web-pages.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>语法高亮不够漂亮？这里有你想要的 Rouge 主题</title>
        <description>&lt;p&gt;写了那么久的代码，找到了满意的代码着色风格吗？想必文本编辑器的代码着色风格你已经找到了中意的了，那么你在网上 post 上去的代码呢？&lt;/p&gt;

&lt;p&gt;Rouge 是一款基于 Ruby 的语法高亮工具，能为你的代码生成漂亮的语法高亮样式。本文将介绍如何使用它，并为大家提供它默认的语法高亮样式预览。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;在-jekyll-中使用-rouge-语法高亮插件&quot;&gt;在 Jekyll 中使用 Rouge 语法高亮插件&lt;/h2&gt;

&lt;p&gt;Jekyll 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;__config.yml&lt;/code&gt; 文件记录了 Jekyll 的最核心配置。其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;markdown&lt;/code&gt; 字段的值表示使用哪一款插件来将 Markdown 文本转换为 HTML 页面结构。&lt;/p&gt;

&lt;p&gt;GitHub 推荐使用的 Jekyll 的 Markdown 插件为 kramdown。kramdown 是一个强大且高性能的文本转换引擎，你可以通过阅读 &lt;a href=&quot;http://gohom.win/2015/11/06/Kramdown-note/&quot;&gt;kramdown 和 markdown 较大的差异比较 - Hom&lt;/a&gt; 了解 kramdown 的强大之处。&lt;/p&gt;

&lt;p&gt;不过，我们现在关系的是它可以使用的语法高亮工具 —— Rouge。在 Jekyll 的配置文件中这样配置它们：&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kramdown&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kramdown&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GFM&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;syntax_highlighter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rouge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;input: GFM&lt;/code&gt; 指的是 &lt;a href=&quot;https://github.github.com/gfm/&quot;&gt;GitHub Flavored Markdown Spec&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;Rouge 支持的语言可以前往此处查看：&lt;a href=&quot;http://rouge.jneen.net/&quot;&gt;Rouge&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;生成-rouge-语法高亮样式&quot;&gt;生成 Rouge 语法高亮样式&lt;/h2&gt;

&lt;p&gt;当然，以上配置只是告诉 kramdown 转换引擎在转换 Markdown 为 HTML 的时候，使用 rouge 格式的样式（具体只语法高亮所用的 css 的 class）。我们需要另外使用 rougify 工具生成对应的样式文件才行。&lt;/p&gt;

&lt;p&gt;你需要先配好 Ruby 环境。如果没有配好，推荐阅读 &lt;a href=&quot;/post/setup-jekyll-in-windows&quot;&gt;快速在 Windows 上搭建 Jekyll 开发环境&lt;/a&gt; 快速配置。&lt;/p&gt;

&lt;p&gt;随后，你便可以使用命令来安装 Rouge。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;rouge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装之后，使用以下命令查看自带的样式有哪些：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rougify &lt;span class=&quot;nb&quot;&gt;help &lt;/span&gt;style
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后得到的输出中可以得知样式有很多种。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;usage: rougify style &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&amp;lt;theme-name&amp;gt;] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&amp;lt;options&amp;gt;]

Print CSS styles &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the given theme.  Extra options are
passed to the theme.  Theme defaults to thankful_eyes.

options:
  &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;default: .highlight&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; a css selector to scope by

available themes:
  base16, base16.dark, base16.light, base16.monokai, base16.monokai.dark, base16.monokai.light, base16.solarized, base16.solarized.dark, base16.solarized.light, colorful, github, gruvbox, gruvbox.dark, gruvbox.light, igorpro, molokai, monokai, monokai.sublime, thankful_eyes, tulip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用以下命令生成一个 github 风格的样式到 assets/css/syntax.css 文件中：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rougify style github &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; assets/css/syntax.css
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;别忘了在你的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 中把这份 css 文件加进去哦！&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ &quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;prepend:&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;baseurl&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;}}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rouge-自带语法高亮主题预览&quot;&gt;Rouge 自带语法高亮主题预览&lt;/h2&gt;

&lt;p&gt;虽然 Rouge 自带了很多种不同的语法高亮样式，但都没有办法直接看到语法高亮的效果。于是我尝试了一些，并贴出了我的 C# 代码在 Rouge 自带语法高亮主题下的效果。&lt;/p&gt;

&lt;p&gt;一般来说很难找到一种语法高亮适用于各种语言，所以选择的时候推荐选一个差不多的，然后再慢慢改。&lt;/p&gt;

&lt;p&gt;以下每张图片的后面都标注了这种风格主题再 rouge 中的名称，使用上一节中提到的命令可以生成语法高亮样式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-10-28.png&quot; alt=&quot;github&quot; /&gt;&lt;br /&gt;
▲ github 需要额外设置前景色 &lt;code class=&quot;highlighter-rouge&quot;&gt;#24292e&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-03-49.png&quot; alt=&quot;colorful&quot; /&gt;&lt;br /&gt;
▲ colorful&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-13-59.png&quot; alt=&quot;monokai.sublime&quot; /&gt;&lt;br /&gt;
▲ monokai.sublime&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-15-26.png&quot; alt=&quot;tulip&quot; /&gt;&lt;br /&gt;
▲ tulip&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-16-08.png&quot; alt=&quot;thankful_eyes&quot; /&gt;&lt;br /&gt;
▲ thankful_eyes&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-17-22.png&quot; alt=&quot;monokai&quot; /&gt;&lt;br /&gt;
▲ monokai&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-18-25.png&quot; alt=&quot;molokai&quot; /&gt;&lt;br /&gt;
▲ molokai&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-20-10.png&quot; alt=&quot;igorpro&quot; /&gt;&lt;br /&gt;
▲ igorpro&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-21-30.png&quot; alt=&quot;gruvbox.dark&quot; /&gt;&lt;br /&gt;
▲ gruvbox.dark&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-21-30.png&quot; alt=&quot;gruvbox&quot; /&gt;&lt;br /&gt;
▲ gruvbox&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-21-29-40.png&quot; alt=&quot;base16&quot; /&gt;&lt;br /&gt;
▲ base16&lt;/p&gt;

&lt;h2 id=&quot;我修改的样式&quot;&gt;我修改的样式&lt;/h2&gt;

&lt;p&gt;我发现我以前的样式与 monokai.sublime 是很接近的。这应该算是巧合，因为此前我是仿我的 VSCode 主题 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme&quot;&gt;One Dark Pro Vivid&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;既然如此，我就直接基于 monokai.sublime 修改好了。我将默认文字颜色从白色 &lt;code class=&quot;highlighter-rouge&quot;&gt;#ffffff&lt;/code&gt; 改成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;#bbbbbb&lt;/code&gt;，然后将 diff 的颜色也修改成 monokai 的样式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-27-22-07-36.png&quot; alt=&quot;monokai 的diff&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/css/syntax.css&quot;&gt;点击下载 syntax.monokai.sublime.css&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://jekyllrb.com/docs/plugins/&quot;&gt;Plugins - Jekyll • Simple, blog-aware, static sites&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gohom.win/2015/11/06/Kramdown-note/&quot;&gt;kramdown 和 markdown 较大的差异比较 - Hom&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.github.com/gfm/&quot;&gt;GitHub Flavored Markdown Spec&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jneen/rouge&quot;&gt;jneen/rouge: A pure-ruby code highlighter that is compatible with pygments http://rouge.jneen.net/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rouge.jneen.net/&quot;&gt;Rouge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:51:50 +0000</pubDate>
        <link>https://blog.walterlv.com/post/available-themes-of-rouge-style.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/available-themes-of-rouge-style.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>为博客或个人站点的 Markdown 添加 LaTeX 公式支持</title>
        <description>&lt;p&gt;LaTeX 是一套排版系统，原生包含对科学和技术型文档内容的支持，而 LaTeX 公式（LaTeX math and equations）则是这种支持中非常重要的一部分。如果能够在博客或个人站点中使用到 LaTeX 的排版系统，或者说只是其中的数学公式部分，对学术性（或者只是使用到了部分数学原理）文章来说将会非常方便。&lt;/p&gt;

&lt;p&gt;本文将推荐一些脚本，以便添加 LaTeX 数学公式的支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;为站点添加-latex-公式支持&quot;&gt;为站点添加 LaTeX 公式支持&lt;/h2&gt;

&lt;p&gt;在你的站点中添加 &lt;code class=&quot;highlighter-rouge&quot;&gt;MathJax.js&lt;/code&gt; 的支持即可。比如添加下面这段代码：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如机器学习中的线性模型：&lt;/p&gt;

\[h_\theta(x) = \theta_1 x_1 + \theta_2 x_2 + ... \theta_n x_n = \sum_{i=1}^n \theta_i x_i\]

&lt;p&gt;以及它的向量形式：&lt;/p&gt;

\[h_\theta(x) = \theta^T x\]

&lt;p&gt;可以使用如下的 LaTeX 公式写出：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$$h_\theta(x) = \theta_1 x_1 + \theta_2 x_2 + ... \theta_n x_n = \sum_{i=1}^n \theta_i x_i$$
$$h_\theta(x) = \theta^T x$$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而你所需做的，仅仅只是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 中加入如上那段 js 脚本。&lt;/p&gt;

&lt;p&gt;如果你希望写出更复杂的 LaTeX 公式，可以参考 &lt;a href=&quot;https://lindexi.oschina.io/lindexi/post/Latex-%E5%85%AC%E5%BC%8F%E9%80%9F%E6%9F%A5.html&quot;&gt;Latex 公式速查&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;为-vscode-编辑器添加-latex-公式支持&quot;&gt;为 VSCode 编辑器添加 LaTeX 公式支持&lt;/h2&gt;

&lt;p&gt;在 VSCode 插件商店中搜索 &lt;em&gt;latex&lt;/em&gt; 可以得到不少的插件，我使用的是目前有 106K 下载量的 &lt;em&gt;Markdown+Math&lt;/em&gt; 插件。&lt;/p&gt;

&lt;p&gt;在 VSCode 中，只需要预览 Markdown，即可看到这样的 LaTeX 公式支持：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-26-10-18-53.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://cushychicken.github.io/easy-latex-in-jekyll&quot;&gt;Easily Add LaTeX Support To Jekyll&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lindexi.oschina.io/lindexi/post/Latex-%E5%85%AC%E5%BC%8F%E9%80%9F%E6%9F%A5.html&quot;&gt;Latex 公式速查&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:51:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-latex-support-for-web-pages.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-latex-support-for-web-pages.html</guid>
        
        
        <category>site</category>
        
        <category>web</category>
        
        <category>vscode</category>
        
      </item>
    
      <item>
        <title>为博客添加可切换的暗色和亮色主题</title>
        <description>&lt;p&gt;不知从什么时候开始，越来越多的小伙伴喜欢在暗色的编辑器中编写代码；于是写博客的小伙伴们也得在博客中顺应这样的潮流，这样才能更接近平时写代码时的环境。&lt;/p&gt;

&lt;p&gt;然而——绝大多数的技术类博客或技术文章都是亮色主题的，代码在其中以和谐但不太好看的亮色存在，或者扎眼但熟悉的暗色存在。这始终觉得不那么舒适。&lt;/p&gt;

&lt;p&gt;于是，作为博主，我决定考虑添加亮色和暗色两种主题色的支持。如果你也喜欢这样的方式，可以读一读本文，快速 get 到修改方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;主题色改变的原理&quot;&gt;主题色改变的原理&lt;/h2&gt;

&lt;p&gt;html/css 带来的样式改变是非常简单的，html 中的 class 对应 css 中的样式即可完成各种各样的风格变化。&lt;/p&gt;

&lt;p&gt;所以，我们考虑在 body 上额外添加一个 class，名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;dark-theme&lt;/code&gt;；运行时动态切换这个 class 的存在与否，我们便能在整个 body 范围之内切换样式。&lt;/p&gt;

&lt;p&gt;而对于 css，我们为每一个与主题色相关的颜色添加一个与之对应的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dark-theme&lt;/code&gt; 样式。那么，我们只需要即时切换 body 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dark-theme&lt;/code&gt; 的出现与否，就能让浏览器为我们使用全新的样式和颜色。&lt;/p&gt;

&lt;h2 id=&quot;编写-css&quot;&gt;编写 css&lt;/h2&gt;

&lt;p&gt;第一个要改变的，当然是背景色了。如果原来的背景色是设置到 &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 上的，那么我们就通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;.dark-theme&lt;/code&gt; 指定一个暗色版的背景色。&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#282c34&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还有前景色。当然，我们只改颜色，其他的不改：&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;ol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.post-inline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#4F4F4F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;ol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.post-inline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，在暗色背景下，我希望标题不需要加粗，只需要更亮即可：&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;700&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;normal&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.dark-theme&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.post-content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;像这样依次改下去，直到整个页面的暗色看起来都比较协调。&lt;/p&gt;

&lt;p&gt;当然，如果希望立即能够看到效果，应该在 &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 上加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;dark-theme&lt;/code&gt; 这个 class。&lt;/p&gt;

&lt;h2 id=&quot;编写-js&quot;&gt;编写 js&lt;/h2&gt;

&lt;p&gt;其实我们的 js 只有一句话，就是切换 &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; 上的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dark-theme&lt;/code&gt;，所以我选择直接内联。&lt;/p&gt;

&lt;p&gt;我增加了一个按钮，直接在 &lt;code class=&quot;highlighter-rouge&quot;&gt;onclick&lt;/code&gt; 中编写切换 class 的代码：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;切换黑白主题 (beta)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;document.body.classList.toggle('dark-theme');&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;切换黑白主题 (beta)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，只需要点击这个按钮，即可完成黑白主题的切换。&lt;/p&gt;

&lt;h2 id=&quot;处理第三方评论系统这样不支持动态切换主题色的部件&quot;&gt;处理第三方评论系统这样不支持动态切换主题色的部件&lt;/h2&gt;

&lt;p&gt;在我基本上改完之后，发现 Disqus 却没有办法很轻松地改掉。事实上，Disqus 的个人站点设置页面上可以选择亮色或者暗色主题，但是，那是静态的。&lt;/p&gt;

&lt;p&gt;那么如何解决评论系统的问题呢？运行时动态切换吗？似乎没找到方法。&lt;/p&gt;

&lt;p&gt;于是，我们可以使用设计巧妙地规避这个问题。我使用灰色背景替代之前的近黑色背景，然后加上周围的圆角；这样，第三方评论系统的样式便似乎是本就这样设计一样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-19-disqus-theme.gif&quot; alt=&quot;切换 disqus 主题&quot; /&gt;&lt;br /&gt;
▲ 看起来还是很和谐的&lt;/p&gt;

&lt;h2 id=&quot;保存主题色&quot;&gt;保存主题色&lt;/h2&gt;

&lt;p&gt;简单的保存基本上就是使用 cookie，于是我准备了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;theme=dark&lt;/code&gt; 这样的键值对。如果存在，则使用暗色，否则使用亮色。并且，在切换时设置 cookie。&lt;/p&gt;

&lt;p&gt;于是完整的切换代码就像这样：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;切换黑白主题 (beta)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(function(){
    document.body.classList.toggle('dark-theme');
    if (document.body.classList.contains('dark-theme')) { document.cookie = 'theme=dark'; }
    else { document.cookie = 'theme=light'; }
})()&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;切换黑白主题 (beta)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;theme=light&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dark-theme&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;试试点击本文上面的“切换黑白主题”按钮吧！&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:50:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-light-dark-theme-support-for-blogs.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-light-dark-theme-support-for-blogs.html</guid>
        
        
        <category>site</category>
        
        <category>html</category>
        
        <category>javascript</category>
        
        <category>css</category>
        
      </item>
    
      <item>
        <title>转义，解决花括号在 Jekyll 被识别成 Liquid 代码的问题</title>
        <description>&lt;p&gt;在 &lt;a href=&quot;/post/xaml/how-to-use-dependencyproperty-unsetvalue.html&quot;&gt;DependencyProperty.UnsetValue 的正确打开方式&lt;/a&gt; 和 &lt;a href=&quot;/post/jekyll/jekyll-concat.html&quot;&gt;合并 Jekyll 多种类型的页面&lt;/a&gt; 这两篇博客中，我都遇到了代码中的花括号被 Jekyll 识别为 Liquid 代码的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;然而 Liquid 的问题还需 Liquid 来解。&lt;/p&gt;

&lt;p&gt;而 Liquid 的 raw 就是用来解决这个问题的。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{% raw %}
{% comment %} 这里是各种包含奇怪花括号 {{{0}}} 的地方 {% endcomment %}
{% endraw %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://xiaohuang.rocks/2016/03/16/b-jekyll/&quot;&gt;Jekyll 大括号 {% %} 转义 · Xiao&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:49:15 +0000</pubDate>
        <link>https://blog.walterlv.com/post/jekyll/raw-in-jekyll.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/jekyll/raw-in-jekyll.html</guid>
        
        <category>jekyll</category>
        
        <category>liquid</category>
        
        <category>raw</category>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>合并 Jekyll 多种类型的页面</title>
        <description>&lt;p&gt;以前胡思乱想时，有时会讲给小伙伴们听，有时会将想法在微信上发给自己，但多数时候是没有后文的，让胡思乱想烂在脑中。还好多数时候我记得，就像我亲自记得 3 岁时候的一些故事一样。&lt;/p&gt;

&lt;p&gt;但今天大脑被一些凌乱的事情撑爆了，心情极度低落。正好近期学着写博客，于是想把一些胡思乱想的事情写在自己的站点上。&lt;/p&gt;

&lt;p&gt;阅读本文，将学到如何用 Jekyll 做多种类型的页面，并在首页的列表中将这些不同种类的页面合并按日期排序。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;制作除博客之外的新页面类型&quot;&gt;制作除博客之外的新页面类型&lt;/h2&gt;

&lt;p&gt;Jekyll 不止支持博客（post）页面类型，也可以支持自定义页面类型。当然博客是它唯一的内建类型（hard-coded type）。&lt;/p&gt;

&lt;p&gt;我希望独立于博客写一些其他的胡思乱想的随笔。为了避免影响到正常博客的列表，我决定采用自定义页面类型。&lt;/p&gt;

&lt;h4 id=&quot;第一步在-_configyml-文件中添加自定义页面类型集合&quot;&gt;&lt;strong&gt;第一步：在 _config.yml 文件中添加自定义页面类型集合&lt;/strong&gt;&lt;/h4&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;article&lt;/code&gt; 是我为自定义类型取的名称。&lt;/p&gt;

&lt;h4 id=&quot;第二步添加自定义页面类型文件夹&quot;&gt;&lt;strong&gt;第二步：添加自定义页面类型文件夹&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;需要在 Jekyll 项目根目录建立一个 _article 文件夹，此名称与第一步的类型名称一致，前面加下划线。&lt;/p&gt;

&lt;p&gt;此后，在这个文件夹里放跟 _posts 文件夹中一样规则的文件用于写文章。&lt;/p&gt;

&lt;h4 id=&quot;可选第三步添加自定义页面类型默认元数据&quot;&gt;&lt;strong&gt;（可选）第三步：添加自定义页面类型默认元数据&lt;/strong&gt;&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;defaults:
  - scope:
      path: &quot;_article&quot;
      type: &quot;article&quot;
    values:
      layout: &quot;post&quot;
      author: &quot;walterlv 吕毅&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里我让 article 类型使用 post 类型的页面布局。&lt;/p&gt;

&lt;h4 id=&quot;可选第四步添加自定义页面类型的页面列表&quot;&gt;&lt;strong&gt;（可选）第四步：添加自定义页面类型的页面列表&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;就像 posts 列表的页面一样制作一个 article 列表。&lt;/p&gt;

&lt;p&gt;可以参考我的 posts 布局文件和 article 布局文件，两者几乎一样都是可以的，只是 article 遍历的时候使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;site.article&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;制作一个合并了博客和其他页面类型的页面列表&quot;&gt;制作一个合并了博客和其他页面类型的页面列表&lt;/h2&gt;

&lt;p&gt;我希望在首页中混杂我的博客和胡思乱想，于是必须将两种不同类型的集合合并。&lt;/p&gt;

&lt;p&gt;使用如下代码：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{% assign all_posts = &quot;&quot; | split: &quot;&quot; %}
{% for article in site.article %}
    {% assign all_posts = all_posts | push: article %}
{% endfor %}
{% for post in site.posts %}
    {% assign all_posts = all_posts | push: post %}
{% endfor %}
{% assign all_posts = all_posts | sort: date | reverse %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于 Jekyll 没有 concat 方法，所以只好一个个地将集合项添加进新集合。集合生成好后，按照日期排序。&lt;/p&gt;

&lt;p&gt;此后，遍历以生成列表的时候使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;all_posts&lt;/code&gt; 集合即可。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/BryanSchuetz/52012affd9318ba59e19a74639a8c16a&quot;&gt;Concat arrays in Jekyll(liquid)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll/issues/2515&quot;&gt;Sorting &amp;amp; ordering collections · Issue #2515 · jekyll/jekyll&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/12465521/for-loops-in-liquid-using-reversed-in-conjunction-with-limit1&quot;&gt;jekyll - For loops in Liquid: using reversed in conjunction with limit:1 - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:49:01 +0000</pubDate>
        <link>https://blog.walterlv.com/post/jekyll/jekyll-concat.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/jekyll/jekyll-concat.html</guid>
        
        <category>jekyll</category>
        
        <category>concat</category>
        
        <category>liquid</category>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>让 GitHub Pages 强制使用 HTTPS（含码云的 gitee/oschina.io）</title>
        <description>&lt;p&gt;一天晚上在手机上浏览自己的博客时，发现居然充斥着各种恶心的广告！顿时内心犹如一万只神兽呼啸而过，不过又能怪谁呢？！&lt;/p&gt;

&lt;p&gt;为避免引起读者不适，不贴图，只放链接，感兴趣自己点开看：&lt;a href=&quot;/static/posts/2017-09-17-ads-over-http-2.png&quot;&gt;图 2&lt;/a&gt;、&lt;a href=&quot;/static/posts/2017-09-17-ads-over-http-1.png&quot;&gt;图 1&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;本文的重点其实是括号里的码云（gitee.io）。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;github-pages&quot;&gt;GitHub Pages&lt;/h2&gt;

&lt;p&gt;去自己的 GitHub Pages 仓库页找了找设置项（https://github.com/walterlv/walterlv.github.io/settings），果然发现了有强制 https 设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-44-17.png&quot; alt=&quot;Enforce HTTPS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;开启后再打开 &lt;a href=&quot;https://walterlv.github.io/&quot;&gt;walterlv.github.io&lt;/a&gt;，果然 https 了。&lt;/p&gt;

&lt;h2 id=&quot;码云的-pages-服务&quot;&gt;码云的 Pages 服务&lt;/h2&gt;

&lt;p&gt;GitHub Pages 设置得这么轻松，想必码云的 Pages 服务应该也不难吧……&lt;/p&gt;

&lt;p&gt;去这里找：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-49-03.png&quot; alt=&quot;Pages 服务&quot; /&gt;&lt;/p&gt;

&lt;p&gt;没有。去设置里找，还是没有……&lt;/p&gt;

&lt;p&gt;于是去码云 QQ 群里问了问，得到答复是直接在地址栏输入 &lt;a href=&quot;https://walterl.gitee.io/&quot;&gt;https://walterl.gitee.io&lt;/a&gt; 就会是 https 的。&lt;/p&gt;

&lt;p&gt;可是，大多数读者怎么会去注意到去输入 https 呢？只好做重定向了。&lt;/p&gt;

&lt;p&gt;于是在 Jekyll 的 GitHub 仓库中找到有人在讨论此问题：&lt;a href=&quot;https://github.com/jekyll/jekyll-redirect-from/issues/18&quot;&gt;https://github.com/jekyll/jekyll-redirect-from/issues/18&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;根据其中的讨论，我在所有页面的头文件（其实就是 /_includes/head.html 文件）中写下了这么一段代码：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^http:/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本地跑起来一看，傻眼了，居然本机下是 https://localhost:4000，这肯定无法打开页面啊。好吧，那就对本机多做个判断，于是形成了下面这段代码：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 判断非本机且未使用 https 时，强制重定向到 https。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^http:/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在本文用的就是这个。不信？往上看，把地址栏里 https 的 s 去掉回车，是不是还是 https？&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:48:47 +0000</pubDate>
        <link>https://blog.walterlv.com/jekyll/2017/09/17/force-https-for-github-pages.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/jekyll/2017/09/17/force-https-for-github-pages.html</guid>
        
        
        <category>site</category>
        
        <category>git</category>
        
        <category>github</category>
        
      </item>
    
      <item>
        <title>如何搭建一个基于 GitHub Pages 的 Jekyll 静态博客（目录）</title>
        <description>&lt;p&gt;GitHub Pages 为个人、组织和项目提供了展示一些页面的方式，GitHub 帮助页的 &lt;a href=&quot;https://help.github.com/articles/user-organization-and-project-pages/&quot;&gt;User, Organization, and Project Pages&lt;/a&gt; 页面就有说明。这里，我们使用 GitHub Pages 来搭建自己的博客，正好也是里面说的给 User 用作展示的页面的用途。当然，GitHub 甚至直接在官方页面中告诉大家，你可以用来当作你的博客使用，就是这句 &lt;a href=&quot;https://github.com/blog/272-github-pages&quot;&gt;Create a blog and spread your ideas. Whatever you want!&lt;/a&gt;（译：创建一个博客来传播你的想法，反正想做什么都行！）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文将是一系列文章的目录，用于帮助你搭建一个看起来很专业的让你爱不释手的个人博客。&lt;/p&gt;

&lt;p&gt;有三篇是翻译文章，来自于 &lt;a href=&quot;https://vdaubry.github.io&quot;&gt;Vincent Daubry&lt;/a&gt;，会比较专业；其它是自己的原创文章，用于一些补充。&lt;/p&gt;

&lt;p&gt;目录：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;./2017-09-14-setup-a-jekyll-blog-1.html&quot;&gt;[译] 搭建一个托管在 GitHub Pages 的 Jekyll 博客，并添加 Disqus 评论功能&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;[原] &lt;a href=&quot;https://vdaubry.github.io/2014/10/19/setup-a-jekyll-blog/&quot;&gt;Setup up a jekyll blog using github pages and disqus comments&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;2017-09-14-setup-a-jekyll-blog-on-windows.html&quot;&gt;在 Windows 系统上安装 Jekyll 环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./2017-09-14-setup-a-jekyll-blog-2.html&quot;&gt;[译] 为 Jekyll 博客添加社交分享按钮&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;[原] &lt;a href=&quot;https://vdaubry.github.io/2014/10/20/add-social-sharing-buttons-with-jekyll/&quot;&gt;Add social sharing buttons to your Jekyll blog&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./2017-09-14-setup-a-jekyll-blog-3.html&quot;&gt;[译] 为 Jekyll 博客做搜索引擎优化（SEO）&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;[原] &lt;a href=&quot;https://vdaubry.github.io/2014/10/21/SEO-for-your-Jekyll-blog/&quot;&gt;SEO for your Jekyll blog&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./2017-09-17-force-https-for-github-pages.html&quot;&gt;将 GitHub Pages 设置为 https 安全链接&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:48:37 +0000</pubDate>
        <link>https://blog.walterlv.com/jekyll/2017/09/15/setup-a-jekyll-blog.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/jekyll/2017/09/15/setup-a-jekyll-blog.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>[译] 搭建一个托管在 GitHub Pages 的 Jekyll 博客，并添加 Disqus 评论功能</title>
        <description>&lt;p&gt;本文翻译自 &lt;a href=&quot;https://vdaubry.github.io/2014/10/19/setup-a-jekyll-blog/&quot;&gt;Setup up a jekyll blog using github pages and disqus comments&lt;/a&gt;，原作者 &lt;a href=&quot;https://vdaubry.github.io&quot;&gt;Vincent Daubry&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;想不想马上就开始搭建个人博客，简单易学，还好看？这篇文章将教你用 &lt;a href=&quot;http://jekyllcn.com/&quot;&gt;Jekyll&lt;/a&gt; 搭建博客，配上一款养眼的主题，然后跑在 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; 上。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;为什么选用静态的站点生成器&quot;&gt;为什么选用静态的站点生成器？&lt;/h2&gt;

&lt;p&gt;相比于使用类似 WordPress 这样的 CMS（译者注：内容管理系统，允许用户在 Web 上创建和发布内容），我们有几条理由来选择使用静态站点生成器。对于本文来说，我们主要关注于 Jekyll 带给我们的简单：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;上手容易（熟悉 Markdown 的话就更好了）&lt;/li&gt;
  &lt;li&gt;相当少的设置&lt;/li&gt;
  &lt;li&gt;部署方便，不需要运行服务器端程序&lt;/li&gt;
  &lt;li&gt;可以直接放到 GitHub 上用（感谢 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;即便是静态站点生成器，&lt;a href=&quot;https://staticsitegenerators.net/&quot;&gt;这里&lt;/a&gt;也列出了很多款，那凭什么选择 Jekyll？&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;这可是当下最流行的静态站点生成器之一（&lt;a href=&quot;https://www.staticgen.com/&quot;&gt;不信看这里&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;GitHub 在用（GitHub 创始人 Tom Preston-Werner 编写）&lt;/li&gt;
  &lt;li&gt;基于 Ruby 生态系统&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;挑选一款-jekyll-博客主题&quot;&gt;挑选一款 Jekyll 博客主题&lt;/h2&gt;

&lt;p&gt;默认的模板在设计上只能说一般般，不过你可以从 &lt;a href=&quot;http://jekyllthemes.org/&quot;&gt;jekyllthemes.org&lt;/a&gt; 找到更棒的模板。你正在阅读的本站博客用的是 &lt;a href=&quot;https://github.com/old-jekyll-templates/Read-Only-Jekyll-Theme&quot;&gt;Read Only&lt;/a&gt; 模板，把它克隆下来你就可以开始啦。（译者注：原文博客用的模板是 &lt;a href=&quot;https://github.com/BlackrockDigital/startbootstrap-clean-blog-jekyll&quot;&gt;clean-blog&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;强烈推荐把每个页面顶部那张默认的大图换一张，在 &lt;a href=&quot;http://www.sitebuilderreport.com/stock-up&quot;&gt;stock-up&lt;/a&gt; 你可以找到很多基于&lt;a href=&quot;https://creativecommons.org/licenses/?lang=zh&quot;&gt;知识共享许可协议&lt;/a&gt;的高清大图。&lt;/p&gt;

&lt;p&gt;为了在本地预览效果，你需要先安装 Jekyll，参见&lt;a href=&quot;http://jekyllcn.com/&quot;&gt;这里&lt;/a&gt;。不过总体来说再复杂也只是这一句：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jekyll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;（译者注：然而 Windows 系统上复杂一点点，上面那个命令在 Windows 上其实是跑不起来的，如果你没有配好环境，请先阅读 &lt;a href=&quot;/post/setup-jekyll-in-windows&quot;&gt;快速在 Windows 上搭建 Jekyll 开发环境&lt;/a&gt;。）&lt;/p&gt;

&lt;p&gt;为了让 Jekyll 在你的电脑上跑起来，请阅读 &lt;a href=&quot;http://jekyllcn.com/docs/usage/&quot;&gt;Jekyll 基本用法&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;部署-jekyll-博客&quot;&gt;部署 Jekyll 博客&lt;/h2&gt;

&lt;p&gt;要部署你的博客，你只需要做以下任意一件事即可：&lt;/p&gt;

&lt;h4 id=&quot;1-手动生成静态博客&quot;&gt;1. 手动生成静态博客&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jekyll build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行完后会将静态页面全部生成到 _site 文件夹下，然后把这个文件夹用 HTML 服务进行托管即可：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在远程服务器上运行 Apache&lt;/li&gt;
  &lt;li&gt;扔到亚马逊 S3 上作为静态站点&lt;/li&gt;
  &lt;li&gt;拷贝到 Dropbox 上作为公开的文件夹（不确定有没有用，算了还是别试了……）（译者注：这句话是原作者说的，不关我的事……）&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;2-用-github-pages-托管而你需要做的只是在-github-上建一个这种名字的仓库&quot;&gt;2. 用 GitHub Pages 托管，而你需要做的只是在 GitHub 上建一个这种名字的仓库：&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;your_github_username.github.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每次你把你的 Jekyll 博客仓库推送到 GitHub 仓库上，GitHub 就会自动为你生成和部署静态站点。&lt;/p&gt;

&lt;h2 id=&quot;使用-disqus-为你的博客添加评论功能&quot;&gt;使用 Disqus 为你的博客添加评论功能&lt;/h2&gt;

&lt;p&gt;添加 Disqus 评论功能非常简单：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;去 &lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus&lt;/a&gt; 创建个账号&lt;/li&gt;
  &lt;li&gt;一步步开通 Disqus 站点账号之后，进入到 Universal Code install instructions 页面&lt;/li&gt;
  &lt;li&gt;将 Disqus 提供的代码贴到 _layout / post.html 文件里面&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(译者注：国内接入社会化评论需取得 ICP 备案，也就是说随着国内使用人数的增多，Disqus 随时有被屏蔽的可能性。)&lt;/p&gt;

&lt;h2 id=&quot;添加发邮件功能&quot;&gt;添加发邮件功能&lt;/h2&gt;

&lt;p&gt;Clean-Blog 模板（译者注：就是原文博主用的那个模板）自带一个非常棒的“联系我”页，不过他发邮件用的是 PHP 脚本，GItHub Pages 不会执行任何 PHP 脚本，所以这对我们来说根本没用。&lt;/p&gt;

&lt;p&gt;不过好在还有其他很多提供邮件发送功能的服务商可用，我选的是 &lt;a href=&quot;https://formspree.io/&quot;&gt;formspree&lt;/a&gt; 因为他简单还免费。&lt;/p&gt;

&lt;p&gt;因为我并不需要验证整个邮件表单是否有效（只需要验证值的合法性、检查下邮件格式对不对），所以我直接把 jqBootstrapValidation 删掉只用纯 HTML5 验证。&lt;/p&gt;

&lt;p&gt;“联系我”表单使用 JavaScript 提交，所以我依然保留了这部分代码，然后稍稍做了点修改：&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#contactForm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//formspree.io/my@email.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dataType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// Success message&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;div class='alert alert-success'&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;button type='button' class='close' data-dismiss='alert' aria-hidden='true'&amp;gt;&amp;amp;times;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;strong&amp;gt;Your message has been sent. &amp;lt;/strong&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

          &lt;span class=&quot;c1&quot;&gt;//clear all fields&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#contactForm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// Fail message&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;div class='alert alert-danger'&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-danger&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;button type='button' class='close' data-dismiss='alert' aria-hidden='true'&amp;gt;&amp;amp;times;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-danger&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;strong&amp;gt;Sorry it seems that my mail server is not responding. Please try again later!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#success &amp;gt; .alert-danger&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;//clear all fields&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#contactForm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;于是现在你不用写后端也能发邮件啦！&lt;/p&gt;

&lt;p&gt;以上。&lt;/p&gt;

&lt;p&gt;如果你觉得这篇教程还有提升空间，欢迎留言评论。&lt;/p&gt;

&lt;p&gt;本文源码在这里：&lt;a href=&quot;https://github.com/vdaubry/vdaubry.github.io&quot;&gt;vdaubry.github.io&lt;/a&gt;（译者注：翻译版的在这里 &lt;a href=&quot;https://github.com/walterlv/walterlv.github.io&quot;&gt;walterlv.github.io&lt;/a&gt;）&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Aug 2018 06:46:55 +0000</pubDate>
        <link>https://blog.walterlv.com/jekyll/2017/09/15/setup-a-jekyll-blog-1.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/jekyll/2017/09/15/setup-a-jekyll-blog-1.html</guid>
        
        
        <category>site</category>
        
      </item>
    
      <item>
        <title>使用 C# 代码创建快捷方式文件</title>
        <description>&lt;p&gt;快捷方式是一种特殊的文件，扩展名为 lnk。有很多种方式来创建快捷方式，不过使用 C# 代码创建一个却并不那么容易。&lt;/p&gt;

&lt;p&gt;本文分享三种不同的方式创建快捷方式。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;随处可用的代码&quot;&gt;随处可用的代码&lt;/h2&gt;

&lt;p&gt;这是最方便的方式了，因为这段代码随便放到一段代码中就能运行：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 为当前正在运行的程序创建一个快捷方式。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;lnkFilePath&quot;&amp;gt;快捷方式的完全限定路径。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;args&quot;&amp;gt;快捷方式启动程序时需要使用的参数。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lnkFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shellType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTypeFromProgID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WScript.Shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;dynamic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shellType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lnkFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Arguments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码为当前正在运行的程序创建一个快捷方式。当然，如果你希望给其他文件创建快捷方式，就改一改里面的代码吧，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetPath&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkingDirectory&lt;/code&gt; 改为其他参数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-20-37-14.png&quot; alt=&quot;快捷方式属性&quot; /&gt;&lt;br /&gt;
▲ 快捷方式属性（其中 Target 等同于上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetPath&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Arguments&lt;/code&gt; 一起，Start in 等同于上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkingDirectory&lt;/code&gt;）&lt;/p&gt;

&lt;h3 id=&quot;引用-com-组件&quot;&gt;引用 COM 组件&lt;/h3&gt;

&lt;p&gt;引用 COM 组件 Interop.IWshRuntimeLibrary.dll 能够获得类型安全，不过本质上和以上方法是一样的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lnkFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWshRuntimeLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WshShell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IWshRuntimeLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IWshShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linkFileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Arguments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkingDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;兼容-net-35-或早期版本&quot;&gt;兼容 .NET 3.5 或早期版本&lt;/h3&gt;

&lt;p&gt;如果你还在使用 .NET Framework 3.5 或更早期版本，那真的很麻烦。同情你一下，不过也贴一段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lnkFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shellType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTypeFromProgID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WScript.Shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shellType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shellType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CreateShortcut&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvokeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linkFileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcutType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcutType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TargetPath&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEntryAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcutType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Arguments&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcutType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WorkingDirectory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationBase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortcutType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Save&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BindingFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvokeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 11 Aug 2018 01:58:29 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-shortcut-file-using-csharp.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-shortcut-file-using-csharp.html</guid>
        
        
        <category>windows</category>
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>发布了一款库（或工具包），如何持续地编写更新日志（ChangeLog）？</title>
        <description>&lt;p&gt;据说程序员最讨厌的两件事是 “别人没有写文档” 和 “要我写文档”。&lt;/p&gt;

&lt;p&gt;编写更新日志可是也落入此怪圈呢！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;程序员不写文档&quot;&gt;程序员不写文档&lt;/h2&gt;

&lt;p&gt;来自 GitHub 的开源调查问卷结果直接显示，最令人头痛的莫过于文档了：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Incomplete or outdated documentation is a pervasive problem, observed by 93% of respondents, yet 60% of contributors say they rarely or never contribute to documentation. When you run into documentation issues, help a maintainer out and open a pull request that improves them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;▲ 来自 &lt;a href=&quot;http://opensourcesurvey.org&quot;&gt;http://opensourcesurvey.org&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;自动化&quot;&gt;自动化&lt;/h2&gt;

&lt;p&gt;我曾经试图找到一些自动化的方式来生成更新日志，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;查找 git 提交日志&lt;/li&gt;
  &lt;li&gt;查找 issues 问题&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而，这样生成的日志真难看懂！不信你试着把一个项目的 Issues 列表读一遍？&lt;/p&gt;

&lt;h2 id=&quot;更新日志应该包含哪些内容&quot;&gt;更新日志应该包含哪些内容&lt;/h2&gt;

&lt;p&gt;站在库的使用者的角度来看，程序员们希望看到什么样的更新日志，不希望看到什么样的更新日志？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;添加的接口&lt;/li&gt;
  &lt;li&gt;现有接口的改变&lt;/li&gt;
  &lt;li&gt;未来即将删除的接口&lt;/li&gt;
  &lt;li&gt;此版本已经删除的接口&lt;/li&gt;
  &lt;li&gt;此版本修复的 Bug&lt;/li&gt;
  &lt;li&gt;此版本的安全性改进&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然而这些都写了会让编写者很痛苦的……&lt;/p&gt;

&lt;h2 id=&quot;手工和自动化的结合&quot;&gt;手工和自动化的结合&lt;/h2&gt;

&lt;p&gt;当存在 API 比较工具的时候，我们可以很容易地比较各个版本间 API 的变化，包括新增、改变、即将移除和已经移除。而这部分的内容由工具生成是没什么阅读障碍的。&lt;/p&gt;

&lt;p&gt;另一部分，描述功能的手工编写会比较容易阅读。例如新增的功能、修改的功能、已经删除的功能。&lt;/p&gt;

&lt;h2 id=&quot;优秀文档的参考&quot;&gt;优秀文档的参考&lt;/h2&gt;

&lt;p&gt;以下是 UWP 的开发文档，属手工和自动化结合生成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-17-34-12.png&quot; alt=&quot;UWP 文档&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 05 Aug 2018 09:35:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-write-changelog-and-keep-it-updating.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-write-changelog-and-keep-it-updating.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在 GitHub 公开仓库中隐藏自己的私人邮箱地址</title>
        <description>&lt;p&gt;GitHub 重点在开方源代码，其本身还是非常注重隐私的。这一点与面向企业的 GitLab 很不一样。&lt;/p&gt;

&lt;p&gt;不过，你依然可能在 GitHub 上泄露隐私信息，例如企业内部所用的电子邮箱。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;github-对个人隐私的尊重&quot;&gt;GitHub 对个人隐私的尊重&lt;/h2&gt;

&lt;p&gt;git 的设定，开发者需要设置自己的邮箱：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-14-56-56.png&quot; alt=&quot;git 的邮箱设置&quot; /&gt;&lt;br /&gt;
▲ git 的邮箱设置（即便是公开的邮箱，我也不在博客里贴出来）&lt;/p&gt;

&lt;p&gt;而在 GitLab 上，我们可以很直接地在提交上面看到提交者的邮箱：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-14-49-29.png&quot; alt=&quot;GitLab 上的提交信息&quot; /&gt;&lt;br /&gt;
▲ GitLab 上的提交信息（图片已被魔改，毕竟邮箱是隐私）&lt;/p&gt;

&lt;p&gt;但是在 GitHub 上，同样的行为是看不到邮箱的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-14-59-22.png&quot; alt=&quot;GitHub 上的提交信息&quot; /&gt;&lt;br /&gt;
▲ GitHub 上的提交信息（图片原封不动）&lt;/p&gt;

&lt;p&gt;不止是提交信息，在其他的很多页面中，你都不会看到 GitHub 暴露邮箱地址。&lt;/p&gt;

&lt;h2 id=&quot;依然能看到的邮箱地址&quot;&gt;依然能看到的邮箱地址&lt;/h2&gt;

&lt;p&gt;在 GitHub 上可以单独看提交信息，比如你可以去这里看看：&lt;a href=&quot;https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620&quot;&gt;https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620&lt;/a&gt;。里面依然没有邮箱地址。&lt;/p&gt;

&lt;p&gt;然而，当你在地址的最后面加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;.patch&lt;/code&gt; 之后，就变得不一样了：&lt;a href=&quot;https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620.patch&quot;&gt;https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620.patch&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ https://github.com/walterlv/Whitman/commit/1088973f71466aaed1eff7a5fdf00eb7f4604620.patch
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;From 1088973f71466aaed1eff7a5fdf00eb7f4604620 Mon Sep 17 00:00:00 2001
From: walterlv &amp;lt;lvyi@example.com&amp;gt;
Date: Sat, 4 Aug 2018 17:37:01 +0800
Subject: [PATCH] Use Segoe MDL2 Assets font.
&lt;/span&gt;
---
 src/Whitman.Wpf/Themes/Window.Universal.xaml | 24 +++++++-------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/src/Whitman.Wpf/Themes/Window.Universal.xaml b/src/Whitman.Wpf/Themes/Window.Universal.xaml
&lt;span class=&quot;gh&quot;&gt;index 8b78e41..522ab51 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/src/Whitman.Wpf/Themes/Window.Universal.xaml
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/src/Whitman.Wpf/Themes/Window.Universal.xaml
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意第二行，出现了我的邮箱地址。为了脱敏，我将内容替换成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;lvyi@example.com&lt;/code&gt;；如果你想看真正的邮箱地址，请前往真实的网页查看。&lt;/p&gt;

&lt;p&gt;GitHub 在这一点上已经为我们做了很多了，至少查看邮箱地址已经不是普通人可以看得到的了。&lt;/p&gt;

&lt;h2 id=&quot;添加隐私邮箱&quot;&gt;添加隐私邮箱&lt;/h2&gt;

&lt;p&gt;GitHub 提供了两种方法来保护我们的邮箱隐私：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在推送时发现隐私邮箱则阻止推送；&lt;/li&gt;
  &lt;li&gt;使用 GitHub 专用的替代邮箱。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;前往 &lt;a href=&quot;https://github.com/settings/emails&quot;&gt;https://github.com/settings/emails&lt;/a&gt; 可以对自己的邮箱地址进行设置。在 Primary email address 一栏，我们能看到 GitHub 为我们提供了一个专用的用于在 git 中配置的邮箱地址。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-16-45-54.png&quot; alt=&quot;Primary email address&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-16-43-46.png&quot; alt=&quot;邮箱列表&quot; /&gt;&lt;/p&gt;

&lt;p&gt;继续往 GitHub 邮箱设置页面往下看，可以看到两个隐私设置。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;隐私地址转换：如果发现以上列表中的邮箱地址，则会转换为 GitHub 专用的邮箱地址。&lt;/li&gt;
  &lt;li&gt;阻止推送：如果发现暴露了邮箱地址，则阻止推送。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-05-16-51-15.png&quot; alt=&quot;隐私设置&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/settings/emails&quot;&gt;Email settings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 05 Aug 2018 08:56:26 +0000</pubDate>
        <link>https://blog.walterlv.com/post/remove-personal-emails-from-public-repos.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/remove-personal-emails-from-public-repos.html</guid>
        
        
        <category>git</category>
        
        <category>github</category>
        
      </item>
    
      <item>
        <title>WPF 应用完全模拟 UWP 的标题栏按钮</title>
        <description>&lt;p&gt;WPF 自定义窗口样式有多种方式，不过基本核心实现都是在修改 Win32 窗口样式。然而，Windows 上的应用就应该有 Windows 应用的样子嘛，在保证自定义的同时也能与其他窗口样式保持一致当然能最大程度保证 Windows 操作系统上的体验一致性。&lt;/p&gt;

&lt;p&gt;本文将分享一个我自制的标题栏按钮样式，使其与 UWP 原生应用一模一样（同时支持自定义）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/wpf-simulate-native-window-style-using-window-chrome&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome）&lt;/a&gt; 一文中，我使用 WindowChrome 尽可能将 Windows 原生的窗口机制都用上了，试图完全模拟原生窗口的样式。不过，如果自定义了窗口的背景色，那么标题栏那三大金刚键的背景就显得很突兀。&lt;/p&gt;

&lt;p&gt;由于 Win32 原生的方法顶多只支持修改标题栏按钮的背景色，而不支持让标题栏按钮全透明，所以我们只能完全由自己来实现这三个按钮的功能了。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;标题栏的四个按钮&quot;&gt;标题栏的四个按钮&lt;/h2&gt;

&lt;p&gt;一开始我说三个按钮，是因为大家一般都只能看得见三个。但这里说四个按钮，是因为实际实现的时候我们是四个按钮。事实上，Windows 的原生实现也是四颗按钮。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;最小化&lt;/li&gt;
  &lt;li&gt;还原&lt;/li&gt;
  &lt;li&gt;最大化&lt;/li&gt;
  &lt;li&gt;关闭&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当窗口最小化时，显示还原、最大化和关闭按钮。当窗口普通显示时，显示最小化、最大化和关闭按钮，这也是我们见的最多的情况。当窗口最大化时，显示最小化、还原和关闭按钮。&lt;/p&gt;

&lt;h2 id=&quot;自绘标题栏按钮&quot;&gt;自绘标题栏按钮&lt;/h2&gt;

&lt;p&gt;标题栏按钮并不单独存在，所以我直接做了一整个窗口样式。使用此窗口样式，窗口能够模拟得跟 UWP 一模一样。&lt;/p&gt;

&lt;p&gt;以下是模拟的效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-wpf-simulated.gif&quot; alt=&quot;WPF 模拟版本&quot; /&gt;&lt;br /&gt;
▲ WPF 模拟版本&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-uwp-native.gif&quot; alt=&quot;UWP 原生版本&quot; /&gt;&lt;br /&gt;
▲ UWP 原生版本（为避免说我拿同一个应用附图，我选了微软商店应用对比）&lt;/p&gt;

&lt;p&gt;为了使用到这样近乎原生的窗口样式，我们需要两个文件。一个放 XAML 样式，一个放样式所需的逻辑代码。&lt;/p&gt;

&lt;p&gt;因为代码很长，所以我把它们放到了最后。&lt;/p&gt;

&lt;h2 id=&quot;如何使用我制作的原生窗口样式&quot;&gt;如何使用我制作的原生窗口样式&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-16-53-12.png&quot; alt=&quot;项目目录结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当你把我的两份代码文件放入到你的项目中之后，在 App.xaml 中将资源引用即可：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Application.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Themes/Window.Universal.xaml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application.Resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后，在 MainWindow 中就可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;Style=&quot;{StaticResource Style.Window.Universal}&quot;&lt;/code&gt; 使用这份样式。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:themes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Themes&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.SimulateUwp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;800&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;450&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#279EDA&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Style.Window.Universal}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;themes:UniversalWindowStyle.TitleBar&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;themes:UniversalTitleBar&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ForegroundColor=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;InactiveForegroundColor=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFFFFFF&quot;&lt;/span&gt;
                                  &lt;span class=&quot;na&quot;&gt;ButtonHoverForeground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ButtonHoverBackground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#3FFFFFFF&quot;&lt;/span&gt;
                                  &lt;span class=&quot;na&quot;&gt;ButtonPressedForeground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#7FFFFFFF&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ButtonPressedBackground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#3F000000&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/themes:UniversalWindowStyle.TitleBar&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在这里添加你的正常窗口内容 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，我额外提供了 &lt;code class=&quot;highlighter-rouge&quot;&gt;UniversalWindowStyle.TitleBar&lt;/code&gt; 附加属性，用于像 UWP 那样定制标题栏按钮的颜色。如果不设置，效果跟 UWP 默认情况下的效果完全一样。&lt;/p&gt;

&lt;p&gt;下面是这份样式在 &lt;a href=&quot;ms-windows-store://pdp/?productid=9P8LNZRNJX85&quot;&gt;Whitman - Microsoft Store&lt;/a&gt; 应用中实际使用的效果，其中的颜色设置就是上面代码中所指定的颜色：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-08-04-whitman.gif&quot; alt=&quot;Whitman&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;附样式代码文件&quot;&gt;附样式代码文件&lt;/h2&gt;

&lt;p&gt;样式文件 Window.Universal.xaml：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Window.Universal.xaml --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ResourceDictionary&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;xmlns:themes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Themes&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Style.Window.Universal&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style.Resources&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).ForegroundColor, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.InactiveForeground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).InactiveForegroundColor, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.ButtonHoverForeground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).ButtonHoverForeground, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.ButtonHoverBackground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).ButtonHoverBackground, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.ButtonPressedForeground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).ButtonPressedForeground, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SolidColorBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Brush.TitleBar.ButtonPressedBackground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Color=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{Binding Path=(themes:UniversalWindowStyle.TitleBar).ButtonPressedBackground, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;themes:UniversalWindowStyle.TitleBar&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;themes:UniversalTitleBar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowChrome.WindowChrome&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 64 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NonClientFrameEdges=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Left,Bottom,Right&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;UseAeroCaptionButtons=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Template&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Padding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4 1 4 4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleBarPanel&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Top&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;31&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;FrameworkElement.Resources&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{x:Type Button}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Width&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;46&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BorderThickness&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.Foreground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stylus.IsPressAndHoldEnabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stylus.IsFlicksEnabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stylus.IsTapFeedbackEnabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stylus.IsTouchFeedbackEnabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowChrome.IsHitTestVisibleInChrome&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Template&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OverBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 1 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeIcon&quot;&lt;/span&gt;
                                                                   &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Foreground}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Content}&quot;&lt;/span&gt;
                                                                   &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontFamily=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Segoe MDL2 Assets&quot;&lt;/span&gt;
                                                                   &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style.Triggers&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the pointer is over the button. --&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsMouseOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsStylusOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonHoverForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonHoverBackground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the pointer is pressed. --&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AreAnyTouchesOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonPressedForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonPressedBackground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the touch device is pressed. --&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AreAnyTouchesOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonPressedForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.ButtonPressedBackground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style.Triggers&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Style.Button.Close&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BasedOn=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource {x:Type Button}}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style.Triggers&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the pointer is over the button. --&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsMouseOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsStylusOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#E81123&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the pointer is pressed. --&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AreAnyTouchesOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Black&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#F1707A&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- When the touch device is pressed. --&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsPressed&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AreAnyTouchesOver&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger.Conditions&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Black&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Background&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#F1707A&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/MultiTrigger&amp;gt;&lt;/span&gt;
                                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style.Triggers&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/FrameworkElement.Resources&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Title}&quot;&lt;/span&gt;
                                           &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12 0 156 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.Foreground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;StackPanel&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleBarButtonPanel&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Orientation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Horizontal&quot;&lt;/span&gt;
                                            &lt;span class=&quot;na&quot;&gt;Margin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 -1 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;#xE921;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Minimized&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RestoreButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;#xE923;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MaximizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;#xE922;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Maximized&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CloseButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;#xE106;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Style.Button.Close}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.IsTitleBarCloseButton=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/StackPanel&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AdornerDecorator&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AdornerDecorator&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate.Triggers&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Trigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowState&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Maximized&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RootGrid&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Margin&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4 7 4 4&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleBarPanel&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Height&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MaximizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visibility&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Collapsed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Trigger&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Trigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowState&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RestoreButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visibility&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Collapsed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Trigger&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Trigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WindowState&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Minimized&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visibility&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Collapsed&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Trigger&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Trigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IsActive&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;False&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleTextBlock&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.InactiveForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.InactiveForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RestoreButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.InactiveForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MaximizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.InactiveForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CloseButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Foreground&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Brush.TitleBar.InactiveForeground}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Trigger&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate.Triggers&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;逻辑代码文件 Window.Universal.xaml.cs（当然，名字可以随意）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Window.Universal.xaml.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Controls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Themes&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UniversalWindowStyle&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TitleBarProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;TitleBar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniversalTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniversalWindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UniversalTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnTitleBarChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UniversalTitleBar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniversalTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBarProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetTitleBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UniversalTitleBar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBarProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TitleBarButtonStateProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;TitleBarButtonState&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniversalWindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnButtonStateChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTitleBarButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBarButtonStateProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetTitleBarButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TitleBarButtonStateProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsTitleBarCloseButtonProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;IsTitleBarCloseButton&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UniversalWindowStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnIsCloseButtonChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetIsTitleBarCloseButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsTitleBarCloseButtonProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetIsTitleBarCloseButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsTitleBarCloseButtonProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnTitleBarChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotSupportedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TitleBar property should not be null.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnButtonStateChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnIsCloseButtonChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OldValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CloseButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CloseButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CloseButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StateButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTitleBarButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WindowState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CloseButton_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UniversalTitleBar&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ForegroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InactiveForegroundColor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromRgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0x99&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x99&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x99&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ButtonHoverForeground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ButtonHoverBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromRgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0xE6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xE6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xE6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ButtonPressedForeground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ButtonPressedBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromRgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0xCC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xCC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xCC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;兼容-windows-10-之前的系统&quot;&gt;兼容 Windows 10 之前的系统&lt;/h2&gt;

&lt;p&gt;上面的样式中我们使用了 Segoe MDL2 Assets 字体，而这款字体仅 Windows 10 上才有。于是如果我们的应用还要兼容 Windows 10 之前的系统怎么办呢？&lt;/p&gt;

&lt;p&gt;需要改动两个地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;按钮模板中图标的显示方式（从 &lt;code class=&quot;highlighter-rouge&quot;&gt;TextBlock&lt;/code&gt; 改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;按钮图标的指定方式（从字符串改成 &lt;code class=&quot;highlighter-rouge&quot;&gt;StreamGeometry&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OverBorder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BorderThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 1 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Background}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeIcon&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Foreground}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{TemplateBinding Content}&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SnapsToDevicePixels=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MinimizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Minimized&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StreamGeometry&amp;gt;&lt;/span&gt;M 3,8 L 3,9 L 13,9 L 13,8 Z&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StreamGeometry&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RestoreButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Normal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StreamGeometry&amp;gt;&lt;/span&gt;M 3,3 L 3,4 L 13,4 L 13,3 Z M 3,12 L 3,13 L 13,13 L 13,12 Z M 3,4 L 3,12 L 4,12 L 4,4 Z M 12,4 L 12,12 L 13,12 L 13,4 Z&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StreamGeometry&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MaximizeButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.TitleBarButtonState=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Maximized&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StreamGeometry&amp;gt;&lt;/span&gt;M 3,3 L 3,4 L 13,4 L 13,3 Z M 3,12 L 3,13 L 13,13 L 13,12 Z M 3,4 L 3,12 L 4,12 L 4,4 Z M 12,4 L 12,12 L 13,12 L 13,4 Z&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StreamGeometry&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CloseButton&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource Style.Button.Close}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;themes:UniversalWindowStyle.IsTitleBarCloseButton=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;StreamGeometry&amp;gt;&lt;/span&gt;M 3,3 L 3,4 L 4,4 L 4,3 Z M 5,5 L 5,6 L 6,6 L 6,5 Z M 7,7 L 7,9 L 9,9 L 9,7 Z M 9,9 L 9,10 L 10,10 L 10,9 Z M 11,11 L 11,12 L 12,12 L 12,11 Z M 4,4 L 4,5 L 5,5 L 5,4 Z M 6,6 L 6,7 L 7,7 L 7,6 Z M 12,3 L 12,4 L 13,4 L 13,3 Z M 10,10 L 10,11 L 11,11 L 11,10 Z M 12,12 L 12,13 L 13,13 L 13,12 Z M 11,4 L 11,5 L 12,5 L 12,4 Z M 10,5 L 10,6 L 11,6 L 11,5 Z M 9,6 L 9,7 L 10,7 L 10,6 Z M 6,9 L 6,10 L 7,10 L 7,9 Z M 5,10 L 5,11 L 6,11 L 6,10 Z M 4,11 L 4,12 L 5,12 L 5,11 Z M 3,12 L 3,13 L 4,13 L 4,12 Z&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StreamGeometry&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 05 Aug 2018 02:21:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf-simulate-native-window-title-bar-buttons.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf-simulate-native-window-title-bar-buttons.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET 三个字母究竟应该如何大小写？前面的 “.” 什么时候能够去掉？（.NET Standard / dotnet-core / net472）</title>
        <description>&lt;p&gt;本文将解释在 .NET 技术栈中各种不同使用方式下 N E T 三个字母何时大写何时小写；前面的 “.” 什么时候加上，什么时候去掉，什么时候又使用 “dot”。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;net-在技术文档中&quot;&gt;.NET 在技术文档中&lt;/h2&gt;

&lt;p&gt;如果你阅读过 &lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/&quot;&gt;https://docs.microsoft.com/zh-cn/dotnet/&lt;/a&gt; 中的多数 .NET 技术文档，你应该几乎已经注意到了，在所有对大小写敏感的地方，NET 三个字母都是大写的。&lt;/p&gt;

&lt;p&gt;“.NET” 是 .NET 技术栈名称的最官方写法了，如果能写出 “.NET” 且不会产生其他问题的地方，都应该使用 “.NET”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-28-15-45-53.png&quot; alt=&quot;.NET 文档标题&quot; /&gt;&lt;br /&gt;
▲ 首先映入眼帘的，便是 .NET 技术栈中的所有文档标题&lt;/p&gt;

&lt;h2 id=&quot;net-在代码中&quot;&gt;.NET 在代码中&lt;/h2&gt;

&lt;p&gt;.NET 在代码中并不符合 PascalCase 对命名规范的大小写建议。一般来说三个字母无论是单个单词还是多个单词的缩写，在 PascalCase 中都应该是首字母大写，其后全部小写。但在微软的代码中，NET 依然都是全大写的。&lt;/p&gt;

&lt;p&gt;例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt;，去 &lt;a href=&quot;https://github.com/dotnet/sdk/tree/master/src/Tasks/Microsoft.NET.Build.Tasks/targets&quot;&gt;dotnet/sdk - GitHub&lt;/a&gt; 上看，写法都是 NET 全大写的。&lt;/p&gt;

&lt;h2 id=&quot;net-在标识符中&quot;&gt;.NET 在标识符中&lt;/h2&gt;

&lt;p&gt;其实，我这里想说的标识符并不是指类名或方法名，那是上一节 &lt;a href=&quot;/post/case-of-dotnet-writing.html#net-%E5%9C%A8%E4%BB%A3%E7%A0%81%E4%B8%AD&quot;&gt;.NET 在代码中&lt;/a&gt; 所说的内容。这里想说的是，当 .NET 作为用于识别 .NET 某种特征所用的标识符。一般这种标识符有一些命名限制（例如 “.” 开头经常就不符合限制）。&lt;/p&gt;

&lt;p&gt;通常作为这种类型的标识符是大小写不敏感的，于是，微软在文档中对此的惯用写法是&lt;strong&gt;全部小写&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;例如，在 Url 中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/&quot;&gt;https://docs.microsoft.com/zh-cn/dotnet/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前面的 “.” 被改写成了 “dot”。&lt;/p&gt;

&lt;p&gt;例如，在项目的目标框架中作为标识符使用时：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;netstandard2.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;netcoreapp2.1&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;net472&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这时，连前面的 “.” 都直接去掉了。&lt;/p&gt;

&lt;h2 id=&quot;net-在文件系统中&quot;&gt;.NET 在文件系统中&lt;/h2&gt;

&lt;p&gt;在文件系统中，“.” 作为前缀的文件或文件夹在 OSX 和 Linux 上都是有特殊用途的，代表隐藏文件夹。这意味着如果没有特别的安排，尽量不要为常规文件夹使用 “.” 作为前缀。&lt;/p&gt;

&lt;p&gt;这就意味着，如果你想建一个 .NET 文件夹，你应该去掉前面的 “.”。可是去掉之后的辨识度就太低了，看不出来是 .NET 技术栈。那么怎么命名呢？&lt;/p&gt;

&lt;p&gt;这里给一些建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotNET&lt;/code&gt; 适用于有大小写规范的命名中（例如为了跟 Windows/Android/iOS/OSX 这样的名称保持统一）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; 适用于作为普通标识符的命名中（例如为了跟 windows/android/ios/osx 这样的名称保持统一）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;net&lt;/code&gt; 适用于使用缩写的命名中（例如为了跟 win/android/ios/osx 这样的名称保持统一）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;net-作为产品或机构名称的一部分&quot;&gt;.NET 作为产品或机构名称的一部分&lt;/h2&gt;

&lt;p&gt;JetBrains 家的 .NET 团队很喜欢用 &lt;code class=&quot;highlighter-rouge&quot;&gt;dot&lt;/code&gt; 作为软件名称的前缀，例如 dotCover、dotMemory、dotPeek、dotTrace。去 &lt;a href=&quot;https://www.jetbrains.com/&quot;&gt;JetBrains: Developer Tools for Professionals and Teams&lt;/a&gt; 看看很快就能找到这几款软件的名称。&lt;/p&gt;

&lt;p&gt;.NET Core 开源峰会使用 dnc 这样奇怪的缩写，代表 dotnet-core。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;合理的 .NET 写法有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.NET&lt;/code&gt; &lt;em&gt;推荐&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NET&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotNET&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;net&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果与其他相关技术名词进行组合：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.NET Core&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ML.NET&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.NET.Sdk&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet-standard&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;net472&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 30 Jul 2018 11:47:02 +0000</pubDate>
        <link>https://blog.walterlv.com/post/case-of-dotnet-writing.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/case-of-dotnet-writing.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>每次都要重新编译？太慢！让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译</title>
        <description>&lt;p&gt;如果你干预到了项目的编译过程，可能就需要考虑到差量编译了。不然——当你的项目大起来的时候，就会感受到每次都重新编译时，每次重复调试的过程都要进行漫长等待时的绝望和无奈。&lt;/p&gt;

&lt;p&gt;如果你正遭遇差量编译失效，每次都要重新编译的问题，那么阅读本文应该能够帮助你解决问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;msbuild.exe&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 编译项目的方式是一样的，只不过前者使用完整的 .NET Framework，而后者使用 .NET Core。所以后面我们说到 Target 的差量编译的时候，就不再区分这两者了。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个差量编译的例子&quot;&gt;一个差量编译的例子&lt;/h2&gt;

&lt;p&gt;先看一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 的例子，这里例子来源于我的另一篇文章&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;。在例子中，我没有加入任何的差量编译支持。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Inputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildAllProjects);@(Compile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Outputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet walterlv-tool.dll $(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上述例子的作用是在编译期间执行一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv-tool.dll&lt;/code&gt; 的 .NET Core 应用，在命令执行结束之后，将生成一份新的代码文件 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)Doubi.cs&lt;/code&gt; 并加入编译。&lt;/p&gt;

&lt;p&gt;如果你觉得上面的写法非常陌生，或者说不清楚那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 节点的作用，建议先阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;差量编译的关键&quot;&gt;差量编译的关键&lt;/h2&gt;

&lt;p&gt;每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 都有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; 属性，可以设置，也可以不用设置。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当两者都没有指定时，MSBuild 会认定为此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 在每次编译时都会执行&lt;/li&gt;
  &lt;li&gt;当两者都指定时，MSBuild 会认定为此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 需要进行差量执行&lt;/li&gt;
  &lt;li&gt;不能只指定其中的一个而不指定另一个（MSBuild 直接会提示此 Target 没有正确指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，&lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; &lt;strong&gt;必须指定为文件或文件的集合&lt;/strong&gt;。因为差量编译的判定规则是 “&lt;strong&gt;文件存在，且前后两次编译的大小和修改时间相同&lt;/strong&gt;”。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; 的格式都是一组用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 分隔的字符串，每一项都是一个文件的路径。不过不用特别考虑如何使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 拼接，因为当我们使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; 符号时，收集到的每一项便是使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt; 分隔的。例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(Compile)&lt;/code&gt; 表示在 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt; 中每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Compile&lt;/code&gt; 类型的节点。如果不清楚 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Compile&amp;gt;&lt;/code&gt; 的作用，建议建议先阅读&lt;a href=&quot;/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;假设我们指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;@(Compile)&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; 指定为某个 xxx.exe 生成的临时文件的位置（在 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt; 一文中，我假定为了 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)Doubi.cs&lt;/code&gt;），那么 MSBuild 就会在执行此 Target 之前检查所有这些输入输出文件。如果所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Compile&amp;gt;&lt;/code&gt; 节点中对应的文件都没有改变，而且 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)Doubi.cs&lt;/code&gt; 存在且没改变，那么此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 将不需要执行。任何一个文件不满足此条件，则 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 都将重新执行。&lt;/p&gt;

&lt;h2 id=&quot;不是所有的-target-都适合差量编译&quot;&gt;不是所有的 Target 都适合差量编译&lt;/h2&gt;

&lt;p&gt;注意！&lt;strong&gt;不是所有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 都适合设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; 属性&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;在本文前面的例子中，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 是有明确的输入和输出文件的；然而有些 &lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt; 是没有输入输出文件的——他们的输出依赖于其他 Target 的输出。&lt;/p&gt;

&lt;p&gt;例如我们有另一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt;，它的作用是生成一个属性的值，或者一组文件的名字；而另外一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target&amp;gt;&lt;/code&gt; 使用这个属性的值和这组文件。典型的例子如我在&lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt; 中写的那个 NuGet 工具。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Inputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildAllProjects);@(Compile)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Outputs=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet walterlv-tool.dll $(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemoUseResult&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WalterlvDemo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(IntermediateOutputPath)Doubi.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemo&lt;/code&gt; 生成文件，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoUseResult&lt;/code&gt; 使用文件。这时，&lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemo&lt;/code&gt; 适合使用差量编译，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;WalterlvDemoUseResult&lt;/code&gt; 却不适合！&lt;/p&gt;

&lt;p&gt;因为前者已经生成了文件，如果不执行，文件依然存在；但后者一旦不执行，那么我们就会少一个编译的文件。这将导致后续名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;CoreCompile&lt;/code&gt; 的 Target 执行时，发现少了一个文件，将重新执行编译。&lt;/p&gt;

&lt;p&gt;所以前者的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 指定为空字符串，&lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt; 指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;$(IntermediateOutputPath)Doubi.cs&lt;/code&gt;；但是后者不应该指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inputs&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outputs&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Sat, 28 Jul 2018 09:52:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/msbuild-incremental-build.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/msbuild-incremental-build.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>像黑客一样！Chrome 完全键盘操作指南（原生快捷键 + Vimium 插件）</title>
        <description>&lt;p&gt;有那么一波小伙伴，多数时候都不需要用到鼠标，通常他们正好是“黑客”。当你开始使用键盘操作一切时，便能体会到无需用鼠标瞄准按钮时的干脆，无需在键盘和鼠标之间移动手时的轻松。&lt;/p&gt;

&lt;p&gt;Chrome 原生自带大量快捷键，Vimium 在原生的基础上又增加了大量网页操作。结合两者，你完全能摆脱鼠标。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;chrome-原生快捷键&quot;&gt;Chrome 原生快捷键&lt;/h2&gt;

&lt;p&gt;Chrome 原生快捷键估计多数人都能说出其中的一部分出来，例如 F5 刷新，Ctrl+W 关闭标签页。&lt;/p&gt;

&lt;p&gt;这里我列出日常浏览时会用到的快捷键：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F3&lt;/code&gt; 查找 &lt;em&gt;应该没有人不知道吧&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+G&lt;/code&gt; 查找下一条&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+Shift+G&lt;/code&gt; 查找上一条&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F6&lt;/code&gt; 转到地址栏 &lt;em&gt;于是能够立即开始输入新网址&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt; 跳转页面或搜索 &lt;em&gt;应该没有人不知道吧&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+Enter&lt;/code&gt; 加上 www. 前缀和 .com 后缀然后打开网站&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Alt+Enter&lt;/code&gt; 在新标签页中跳转页面或搜索&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+K&lt;/code&gt; 转到地址栏并搜索&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F10&lt;/code&gt; 转到 Chrome 菜单按钮&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F11&lt;/code&gt; 全屏模式&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F12&lt;/code&gt; 打开开发者工具&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+T&lt;/code&gt; 新建标签页&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;配合 Vimium，你可以操作 Chrome 界面上的所有按钮了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-28-07-39-24.png&quot; alt=&quot;看得见的快捷键&quot; /&gt;&lt;br /&gt;
▲ 蓝色表示 Chrome 原生快捷键，橙色表示 Vimium 快捷键&lt;/p&gt;

&lt;p&gt;更多 Chrome 快捷键可以去官网上查阅：&lt;a href=&quot;https://support.google.com/chrome/answer/157179?hl=zh-Hans&quot;&gt;Chrome 键盘快捷键 - Google Chrome帮助&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;vimium-插件&quot;&gt;Vimium 插件&lt;/h2&gt;

&lt;p&gt;继续阅读之前，请先&lt;a href=&quot;https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb?hl=zh-CN&quot;&gt;点此安装 Vimium 插件&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Vimium = Vim + Chromium&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Chromium 是 Chrome 浏览器所用的内核。&lt;/p&gt;

&lt;p&gt;Vim，如果你知道这款编辑器，那就最好了，因为我太懒不想介绍。但如果你不知道，我也不会介绍，因为太懒了；不过你可以看看 &lt;a href=&quot;https://www.zhihu.com/topic/19570193/hot&quot;&gt;知乎&lt;/a&gt;，它是入门门槛高到爆但功能强大到爆的文本编辑器。&lt;/p&gt;

&lt;p&gt;完全键盘操作就靠 Vimium 了。是的，完全可以脱离鼠标！&lt;/p&gt;

&lt;p&gt;如果你正在阅读这篇博客，那么直接按下 “?” 试试！（&lt;em&gt;我想你应该记得要加上 Shift 才能输入 “?” 吧！&lt;/em&gt;）&lt;/p&gt;

&lt;p&gt;于是你打开了 Vimium 的快捷键帮助页面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-01-19-24-34.png&quot; alt=&quot;按下 ? 可以打开 Vimium 的帮助页面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果英文阅读吃力，可以阅读下面我精简过后的中文版：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;页面滚动
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;j&lt;/code&gt; 按住向下滚，直到松开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;k&lt;/code&gt; 按住向上滚，直到松开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gg&lt;/code&gt; 滚到顶部&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;G&lt;/code&gt; 滚到底部&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;d&lt;/code&gt; 向下滚半页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;u&lt;/code&gt; 向上滚半页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt; 按住向左滚，直到松开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;l&lt;/code&gt; 按住向右滚，直到松开&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;页面导航
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;yy&lt;/code&gt; 复制当前标签页的 url&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;p&lt;/code&gt; 在当前标签页粘贴并打开 url&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;P&lt;/code&gt; 在新标签页粘贴并打开 url&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; 在当前页打开链接&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 在新标签页中打开链接&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;H&lt;/code&gt; 后退&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;L&lt;/code&gt; 前进&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;快速启动框
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;o&lt;/code&gt; 检索书签或历史记录，找到网址后打开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;O&lt;/code&gt; 检索书签或历史记录，找到网址后在新标签页中打开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 检索书签，找到网址后打开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; 检索书签，找到网址后在新标签页中打开&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 检索打开的标签页，选择后切换到此标签页&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;页面标签
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t&lt;/code&gt; 打开一个新标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;J&lt;/code&gt; 切换到左边的标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;K&lt;/code&gt; 切换到右边的标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;^&lt;/code&gt; 切换到刚刚访问的标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;g0&lt;/code&gt; 切换到第一个标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;g$&lt;/code&gt; 切换到最后一个标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;yt&lt;/code&gt; 复制当前的标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; 关闭当前标签页&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;X&lt;/code&gt; 恢复刚刚关闭的标签页&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你应该已经注意到了，多数情况下小写字母表示当前标签页，大写字母表示新标签页。而区分大小写也是 Vimium 与 Chrome 原生快捷键很大的一个不同点（不信你试试按下 CapsLock 键）。&lt;/p&gt;

&lt;p&gt;Vimium 不止是快捷键，你应该页注意到上面的 “快速启动框” 了，凭借着模糊搜索，你能迅速定位到你曾经访问过的网页，而无需再用鼠标一个个去翻找了。&lt;/p&gt;

&lt;p&gt;而这么多的快捷键中唯一一个能被别人看出来你是在用 Vimium 的只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt; 了，因为按下后网页上会显示每个链接的快捷键，按下屏幕上新显示的快捷键能够打开链接（或在新标签页中打开链接）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-28-07-40-20.png&quot; alt=&quot;按下 F 后准备跳转页面&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;摆脱鼠标像黑客一样操作-chrome&quot;&gt;摆脱鼠标，像黑客一样操作 Chrome&lt;/h2&gt;

&lt;p&gt;快捷键虽然多，但其实只需要练习几个小时就熟练了，双手不需要再不断在鼠标和键盘之间移动时，你的效率已暗中提高了。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;我会偷偷告诉你我鼠标坏了吗？&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/157179?hl=zh-Hans&quot;&gt;Chrome 键盘快捷键 - Google Chrome帮助&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 27 Jul 2018 23:40:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/use-chrome-like-a-hacker.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/use-chrome-like-a-hacker.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>.NET/C# 使用 Span&lt;T&gt; 为字符串处理提升性能</title>
        <description>&lt;p&gt;.NET Core 2.1 和 C# 7.2 带来了 Span&lt;T&gt; 的原生支持，原本需要使用不安全代码操作的内存块现在可以使用安全的方式来完成。此前在性能和稳定性上需要有所取舍，而现在可以兼得了。&lt;/T&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;简单的例子&quot;&gt;简单的例子&lt;/h2&gt;

&lt;p&gt;先来看一个字符串处理时使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; 的最简单的例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.StringSpan&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameSpan&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个例子是从 &lt;a href=&quot;https://walterlv.github.io/&quot;&gt;https://walterlv.github.io/&lt;/a&gt; 字符串中取出第 8 个字符开始长度为 8 的部分，随后与其它字符串进行拼接。最后，我们得到了拼接的字符串：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-22-00-29-12.png&quot; alt=&quot;Hello walterlv!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这种方式取出字符串替代了 &lt;code class=&quot;highlighter-rouge&quot;&gt;SubString&lt;/code&gt; 这种会额外生成临时字符串的方式。如果上述代码发生在较大或较多文本的处理中，那么反复的拼接将生成大量的临时字符串，造成大量 GC 压力；而使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; 将不会额外生成任何临时字符串。&lt;/p&gt;

&lt;h2 id=&quot;语言框架的支持&quot;&gt;语言/框架的支持&lt;/h2&gt;

&lt;p&gt;然而，只有 .NET Core 2.1 是原生支持字符串的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsSpan&amp;lt;T&amp;gt;&lt;/code&gt; 方法的，.NET Core 2.0、.NET Framework 4.7.2 是不支持的。.NET Core 2.0 可以无视，因为有了 2.1。但 .NET Framework 的低版本却不能无视，因为用户的计算机上通常都是安装低版本的 .NET Framework。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-22-00-32-40.png&quot; alt=&quot;只有 .NET Core 2.1 支持&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而我们可以安装 &lt;a href=&quot;https://www.nuget.org/packages/System.Memory/&quot;&gt;System.Memory&lt;/a&gt;，以在低版本的 .NET 中获得字符串扩展方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;AsSpan&amp;lt;T&amp;gt;&lt;/code&gt; 的支持。&lt;/p&gt;

&lt;p&gt;那么问题来了，低版本的 .NET &lt;code class=&quot;highlighter-rouge&quot;&gt;StringBuilder&lt;/code&gt; 中并没有提供 &lt;code class=&quot;highlighter-rouge&quot;&gt;Append(ReadOnlySpan&amp;lt;char&amp;gt;)&lt;/code&gt; 方法，于是我们即便使用高性能的方式得到了字符串的一个片段，依然无法将其反复进行拼接。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这真是一个悲伤的故事&lt;/strong&gt;！&lt;/p&gt;

&lt;h2 id=&quot;低版本-net-中有限的字符串性能提升&quot;&gt;低版本 .NET 中有限的字符串性能提升&lt;/h2&gt;

&lt;p&gt;缺少了 &lt;code class=&quot;highlighter-rouge&quot;&gt;StringBuilder&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadOnlySpan&amp;lt;char&amp;gt;&lt;/code&gt; 的支持，广泛使用的字符串拼接功能便没有办法获得 Span&lt;T&gt; 的支持。&lt;/T&gt;&lt;/p&gt;

&lt;p&gt;不过，System.Memory 中提供了其它有限的字符串处理支持，来源于以下两个类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Buffers.Text.Utf8Parser&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Buffers.Text.Utf8Formatter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前者提供从 &lt;code class=&quot;highlighter-rouge&quot;&gt;ReadOnlySpan&amp;lt;char&amp;gt;&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Double&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;DateTime&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;Guid&lt;/code&gt; 等类型的解析，后者提供相反的转换。&lt;/p&gt;

&lt;p&gt;期待 Microsoft 在未来版本的 System.Memory 库中提供对字符串拼接在低版本 .NET 生态中的支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/&quot;&gt;Welcome to C# 7.2 and Span - .NET Blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://channel9.msdn.com/Events/Connect/2017/T125&quot;&gt;C# 7.2: Understanding Span - Connect(); 2017 - Channel 9&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-Span-%E5%85%A5%E9%97%A8.html&quot;&gt;C# Span 入门&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 21 Jul 2018 16:51:23 +0000</pubDate>
        <link>https://blog.walterlv.com/post/improve-string-performance-using-span.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/improve-string-performance-using-span.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名</title>
        <description>&lt;p&gt;在 .NET 程序中使用 Win32 函数并不如 C++ 中方便。因为 C# 中不能引入 C++ 中常用的头文件，于是各种方法签名、结构体定义等等都需要各种寻找。然而 PInvoke.net 帮助我们解决了这个问题。本文推荐一款 Visual Studio 插件来帮助我们更快速地插入 Win32 函数签名。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;pinvokenet&quot;&gt;PInvoke.net&lt;/h2&gt;

&lt;p&gt;PInvoke.net 的官方网站是 &lt;a href=&quot;https://www.pinvoke.net/&quot;&gt;https://www.pinvoke.net/&lt;/a&gt;，如果你只是希望临时找一找 P/Invoke 函数调用的方法签名，那么直接去网站就能搜索。不过，如果你期望写代码时能够随时方便地插入，那么安装插件还是非常方便的。&lt;/p&gt;

&lt;p&gt;前往 Visual Studio Marketplace 即可下载安装 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vs-publisher-306627.PInvokenetVisualStudioExtension&quot;&gt;PInvoke.net Visual Studio Extension&lt;/a&gt; 扩展。不过，更推荐直接在 Visual Studio 的“工具-&amp;gt;扩展和更新”里面在线下载安装插件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-22-39-09.png&quot; alt=&quot;PInvoke.net Visual Studio Extension&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下载完关闭所有的 Visual Studio 后，会弹出扩展安装界面，继续安装即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-22-38-12.png&quot; alt=&quot;安装扩展&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用-pinvokenet-扩展&quot;&gt;使用 PInvoke.net 扩展&lt;/h2&gt;

&lt;p&gt;在安装了 PInvoke.net 插件后，可以在顶部菜单栏中寻找到 PInvoke.net 菜单项，里面可以插入 PInvoke 的函数调用签名：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-22-54-08.png&quot; alt=&quot;Insert PInvoke Signatures&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在，我们搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;MoveWindow&lt;/code&gt; 函数：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-22-57-52.png&quot; alt=&quot;MoveWindow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;随后点击 Insert 便在代码中得到了一份 MoveWindow 的 P/Invoke 函数签名。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     The MoveWindow function changes the position and dimensions of the specified window. For a top-level window, the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     position and dimensions are relative to the upper-left corner of the screen. For a child window, they are relative&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     to the upper-left corner of the parent window's client area.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     &amp;lt;para&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///         Go to https://msdn.microsoft.com/en-us/library/windows/desktop/ms633534%28v=vs.85%29.aspx for more&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///         information&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     &amp;lt;/para&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;hWnd&quot;&amp;gt;C++ ( hWnd [in]. Type: HWND )&amp;lt;br /&amp;gt; Handle to the window.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;X&quot;&amp;gt;C++ ( X [in]. Type: int )&amp;lt;br /&amp;gt;Specifies the new position of the left side of the window.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;Y&quot;&amp;gt;C++ ( Y [in]. Type: int )&amp;lt;br /&amp;gt; Specifies the new position of the top of the window.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;nWidth&quot;&amp;gt;C++ ( nWidth [in]. Type: int )&amp;lt;br /&amp;gt;Specifies the new width of the window.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;nHeight&quot;&amp;gt;C++ ( nHeight [in]. Type: int )&amp;lt;br /&amp;gt;Specifies the new height of the window.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;bRepaint&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     C++ ( bRepaint [in]. Type: bool )&amp;lt;br /&amp;gt;Specifies whether the window is to be repainted. If this&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs. This&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     parent window uncovered as a result of moving a child window.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     If the function succeeds, the return value is nonzero.&amp;lt;br /&amp;gt; If the function fails, the return value is zero.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     &amp;lt;br /&amp;gt;To get extended error information, call GetLastError.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user32.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetLastError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bRepaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过，插件内所带的 P/Invoke 函数似乎并不够多，因为对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;DwmSetWindowAttribute&lt;/code&gt; 这样的函数并没有在插件中出现。不过 &lt;a href=&quot;https://www.pinvoke.net/&quot;&gt;https://www.pinvoke.net/&lt;/a&gt; 中是包含的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-22-56-00.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;除了包含 C# 调用所需的函数签名之外，还包含函数签名中所用的结构体或枚举类型定义。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dwmapi.dll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PreserveSig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DwmSetWindowAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DWMWINDOWATTRIBUTE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DWMWINDOWATTRIBUTE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;NCRenderingEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NCRenderingPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TransitionsForceDisabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AllowNCPaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CaptionButtonBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NonClientRtlLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ForceIconicRepresentation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Flip3DPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExtendedFrameBounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HasIconicBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DisallowPeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcludedFromPeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Cloak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Cloaked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FreezeRepresentation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;感谢广大 .NET 的社区开发者帮助收集各种 PInvoke 函数签名；如果你发现了一些没有收录的，也欢迎加入。&lt;/p&gt;
</description>
        <pubDate>Sat, 21 Jul 2018 14:35:49 +0000</pubDate>
        <link>https://blog.walterlv.com/post/pinvoke-net-visual-studio-extension.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/pinvoke-net-visual-studio-extension.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>visualstudio</category>
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>使用 IFTTT 做 RSS 的邮件订阅服务</title>
        <description>&lt;p&gt;IFTTT 是一个奇特的网络服务。它本身没有提供什么功能，但因为它的工作方式类似编程，所以你可以拿它做各种各样难以想象的事情。&lt;/p&gt;

&lt;p&gt;本文将使用 IFTTT 做一个 RSS 的邮件订阅服务。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;ifttt&quot;&gt;IFTTT&lt;/h2&gt;

&lt;p&gt;IFTTT 这种神奇的名字还是需要介绍一下的 —— 读作 [ɪft]，意思是 &lt;strong&gt;If This Then That&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;直接翻译，是“如果这个，那就那个”。这其实挺有趣的，因为这就像编程语言中的 if 语句：&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;This&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;That&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个句子本身并不涉及什么功能，但我们能通过修改这个语句中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;This&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;That&lt;/code&gt; 来达到执行各种功能的效果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-10-57.png&quot; alt=&quot;if this then that&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;做一个-rss-邮件订阅服务&quot;&gt;做一个 RSS 邮件订阅服务&lt;/h2&gt;

&lt;p&gt;首先，前往 IFTTT：&lt;a href=&quot;https://ifttt.com/&quot;&gt;https://ifttt.com/&lt;/a&gt;。你需要注册一个账号，在登录后再进行下面的操作。&lt;/p&gt;

&lt;p&gt;在首页，我们能找到 MyApplets 标签，进去后，我们便可以新建我们的 RSS 邮件订阅服务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-22-43.png&quot; alt=&quot;MyApplets&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 MyApplets 页面，点击 New Applet 新建一个 Applet。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-23-56.png&quot; alt=&quot;New Applet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这时，我们能看到一个大大的 “if +this then that” 的短语。注意到 this 的颜色不同，而且前面有一个加号 —— 这是一个大大的按钮，提醒你当前的步骤是修改 this。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-24-22.png&quot; alt=&quot;if +this then that&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击 this 之后，我们发现 IFTTT 为我们提供了大量的 this 触发源。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-27-36.png&quot; alt=&quot;琳琅满目的触发源&quot; /&gt;&lt;/p&gt;

&lt;p&gt;找到 RSS Feed，随后选择 New feed item。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-33-18.png&quot; alt=&quot;New feed item&quot; /&gt;&lt;/p&gt;

&lt;p&gt;贴上一个 RSS 的链接 &lt;a href=&quot;https://walterlv.github.io/feed.xml&quot;&gt;https://walterlv.github.io/feed.xml&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-34-45.png&quot; alt=&quot;https://walterlv.github.io/feed.xml&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建完成之后，我们又能看到大量的动作：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-36-09.png&quot; alt=&quot;琳琅满目的动作&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选择邮件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-36-52.png&quot; alt=&quot;Send me an email&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后可选修改邮件中的格式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-38-12.png&quot; alt=&quot;Fill in the email form&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完成：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-21-17-39-12.png&quot; alt=&quot;Finish&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样，当我的博客中有新的文章发布的一小时内，邮箱中就可以收到邮件通知了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/IFTTT&quot;&gt;IFTTT - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 21 Jul 2018 09:40:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/rss-email-using-ifttt.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/rss-email-using-ifttt.html</guid>
        
        
        <category>miscellaneous</category>
        
      </item>
    
      <item>
        <title>理解 Roslyn 中的红绿树（Red-Green Trees）</title>
        <description>&lt;p&gt;Roslyn 的 API 是非常易用的。即便如此复杂的 C# 语法，建立的复杂的 C# 语法树，还有其复杂的树遍历和修改过程，也都被其 API 包装得干净简洁。&lt;/p&gt;

&lt;p&gt;而这背后是它的重要设计思路 —— 红绿树。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;红绿树的影子&quot;&gt;红绿树的影子&lt;/h2&gt;

&lt;p&gt;如果你是通过搜索找到这篇文章的，那么至少证明你调试过 Roslyn API 的使用，或者阅读过 Roslyn 的源码。因为正常使用 Roslyn 的 API 时你是看不到红绿树的，这是 Roslyn 的实现细节。但你在调试的时候可能会看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Green&lt;/code&gt; 属性，或者在阅读源码时看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetRed&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-19-20-07-50.png&quot; alt=&quot;调试时看到的绿树&quot; /&gt;&lt;br /&gt;
▲ 调试时看到的绿树&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetRed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;green&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;green&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Interlocked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateRed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetChildPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ Roslyn 中获取红树的源代码&lt;/p&gt;

&lt;p&gt;源代码摘抄自：&lt;a href=&quot;https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs&quot;&gt;roslyn/SyntaxNode.cs at master · dotnet/roslyn&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;roslyn-的设计理念&quot;&gt;Roslyn 的设计理念&lt;/h2&gt;

&lt;p&gt;Roslyn 一开始就将漂亮的 API 作为目标的一部分，同时还要非常高的性能；所以 Roslyn 的开发团队需要找到一种特殊的数据结构来描述语言（如 C#）的语法。这种数据结构要满足这些期望的要求：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不可变（Immutable）&lt;/li&gt;
  &lt;li&gt;树的形式&lt;/li&gt;
  &lt;li&gt;可以容易地访问父节点和子节点&lt;/li&gt;
  &lt;li&gt;可以非常容易地将任何一个节点对应到源代码文件的一段文本区间&lt;/li&gt;
  &lt;li&gt;可重用（Persistent）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最后一个的英文说法是 Persistent，单词的原本意思是“可持久的，连续的”，我把它翻译为“可重用”（Reusable）。Roslyn 的设计中有一个重要的业务需求，希望能够分析源代码文件并在开发者编辑的过程中不断提供建议。也就是说，当我们&lt;strong&gt;连续不断&lt;/strong&gt;地去修改源代码中的文本内容时，Roslyn 也需要具备很高的性能。如果每次编辑代码都去重新解析一次整份源代码，然后全部重新生成整个数据结构，那将是大量的性能浪费；更不可能实时去分析开发者编辑的源码。所以，在 Roslyn 的设计中，希望源代码文本改变时，整棵树中的大多数节点都是能够重复使用的（无需重新生成）。&lt;/p&gt;

&lt;p&gt;而如果将数据结构设计成不可变的（Immutable），那么重用这些节点将会非常容易。当然不止对于 Roslyn，对其它数据来说，不可变也一样有各种好处；比如可以随时重用这份数据的实例而不用担心可能被各个不同的业务模块意外修改，比如天然是线程安全的。&lt;/p&gt;

&lt;p&gt;那么问题来了，到底什么样的数据结构能够在同时满足以上所有的特点的前提下，同时还能设计出简单易用的 API 呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;既然要容易地访问到父节点和子节点，那么我们是先构造父节点还是子节点呢？如果先构造父节点，那子节点还没有创建出来；而先构造子节点，那父节点就没构造出来。我们要求这样的数据结构具有不可变性，所以我们不可能先把它们都构造出来再去修改它们的父子关系。&lt;/li&gt;
  &lt;li&gt;还有，我们也不能随意地去为任何子节点指定新的父节点，因为子节点是不可变的。然而我们同时有希望能够在连续修改的情况下具备较高的性能，如果连修改父节点都不能办到，那也很难重用之前的节点，最终不得不再次重新生成所有的子节点。&lt;/li&gt;
  &lt;li&gt;另外，如果你在源代码文件中插入了一个字符，那么这个字符后面的每一个节点对应的源代码区间都需要改变。然而这非常不利于连续修改，因为随便一个字符的插入都将导致更新大量节点中的文本区间信息。而由于不可变性，我们只能重新生成这些节点而没法儿重用它们。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是 Roslyn 团队就折腾出了“红绿树”（Red-Green Trees）。&lt;/p&gt;

&lt;h2 id=&quot;红绿树&quot;&gt;红绿树&lt;/h2&gt;

&lt;p&gt;红绿树并不是一棵树，而是两棵树。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;绿&lt;/strong&gt;树（the green tree）是不可变的，可重用的，没有父节点的引用。绿树的构建是自下而上的，每一个节点都保存它在文本区间中的字符个数（说通用点是宽度）。如果源代码的内容被编辑，我们只需要重新创建受编辑影响的绿树的部分；相比于重新分析整棵树，其时间复杂度只有 O(log n)。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;红&lt;/strong&gt;树（the red tree）也是不可变的，是围绕绿树而建的外观（参见 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%96%E8%A7%80%E6%A8%A1%E5%BC%8F&quot;&gt;外觀模式&lt;/a&gt;）。红树的构建是自上而下的，但红树只在需要时才会创建，而一旦编辑了源代码文件，红树就直接丢弃不用了。如果有需要，红树就会开始创建；它会根据绿树自上而下计算最新的父节点引用，计算节点最新对应的文本区间。&lt;/p&gt;

&lt;p&gt;这两棵树设计起来协同工作，前者负责解决 Roslyn 语法分析的性能问题，后者负责对开发人员提供友好的 API 调用。由于最开始 Roslyn 团队的大佬们在会议室讨论时，前者是用红笔画的，后者是用绿笔画的，于是就合在一起称作“红绿树”。&lt;/p&gt;

&lt;p&gt;自此，Roslyn 团队设计出的这种数据结构满足了以上所有的要求。不过，如果红树太大，每次重新生成依然会耗费比较多的性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/ericlippert/2012/06/08/persistence-facades-and-roslyns-red-green-trees/&quot;&gt;Persistence, Facades and Roslyn’s Red-Green Trees – Fabulous Adventures In Coding&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%96%E8%A7%80%E6%A8%A1%E5%BC%8F&quot;&gt;外觀模式 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 19 Jul 2018 11:48:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/the-red-green-tree-of-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/the-red-green-tree-of-roslyn.html</guid>
        
        
        <category>roslyn</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>Roslyn 语法树中的各种语法节点及每个节点的含义</title>
        <description>&lt;p&gt;使用 Roslyn 进行源码分析时，我们会对很多不同种类的语法节点进行分析。如果能够一次性了解到各种不同种类的语法节点，并明白其含义和结构，那么在源码分析的过程中将会更加得心应手。&lt;/p&gt;

&lt;p&gt;本文将介绍 Roslyn 中各种不同的语法节点、每个节点的含义，以及这些节点之间的关系和语法树结构。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;基本概念&quot;&gt;基本概念&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello Walterlv!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上是一个非常简单但完整的 .cs 文件。&lt;/p&gt;

&lt;p&gt;在 Roslyn 的解析中，这就是一个“编译单元”（Compilation Unit）。编译单元是 Roslyn 语法树的根节点。紧接着的 &lt;code class=&quot;highlighter-rouge&quot;&gt;using System&lt;/code&gt; 是 using 指令（Using Directives）；随后是命名空间声明（Namespace Declaration），包含子节点类型声明（Class Declaration）；类型声明包含子节点方法声明（Method Declaration）。&lt;/p&gt;

&lt;p&gt;接下来，我们会介绍 Roslyn 语法树中各种不同种类的节点，以及其含义。&lt;/p&gt;

&lt;h2 id=&quot;语法节点&quot;&gt;语法节点&lt;/h2&gt;

&lt;h3 id=&quot;语法树&quot;&gt;语法树&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CompilationUnit&lt;/strong&gt;，是语法树的根节点。&lt;/p&gt;

&lt;h3 id=&quot;关键字&quot;&gt;关键字&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;UsingKeyword&lt;/strong&gt;、&lt;strong&gt;NamespaceKeyword&lt;/strong&gt;、&lt;strong&gt;PublicKeyword&lt;/strong&gt;、&lt;strong&gt;InternalKeyword&lt;/strong&gt;、&lt;strong&gt;PrivateKeyword&lt;/strong&gt;、&lt;strong&gt;ProtectedKeyword&lt;/strong&gt;、&lt;strong&gt;StaticKeyword&lt;/strong&gt;、&lt;strong&gt;ClassKeyword&lt;/strong&gt;、&lt;strong&gt;InterfaceKeyword&lt;/strong&gt;、&lt;strong&gt;StructKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 C# 的各种关键字：&lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;namespace&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;public&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;private&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;protected&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;static&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;class&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;interface&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;InKeyword&lt;/strong&gt;、&lt;strong&gt;OutKeyword&lt;/strong&gt;、&lt;strong&gt;RefKeyword&lt;/strong&gt;、&lt;strong&gt;ReturnKeyword&lt;/strong&gt;、&lt;strong&gt;ConstKeyword&lt;/strong&gt;、&lt;strong&gt;DefaultKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 C# 的另一波关键字 &lt;code class=&quot;highlighter-rouge&quot;&gt;in&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ref&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;const&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ByteKeyword&lt;/strong&gt;、&lt;strong&gt;CharKeyword&lt;/strong&gt;、&lt;strong&gt;IntKeyword&lt;/strong&gt;、&lt;strong&gt;LongKeyword&lt;/strong&gt;、&lt;strong&gt;BoolKeyword&lt;/strong&gt;、&lt;strong&gt;FloatKeyword&lt;/strong&gt;、&lt;strong&gt;DoubleKeyword&lt;/strong&gt;、&lt;strong&gt;DecimalKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 C# 中的基元类型关键字&lt;code class=&quot;highlighter-rouge&quot;&gt;byte&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;long&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;bool&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;decimal&lt;/code&gt;。需要注意的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;var&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dynamic&lt;/code&gt; 并不是基元类型关键字，在语法节点中，它是 IdentifierName。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AsyncKeyword&lt;/strong&gt;、&lt;strong&gt;AwaitKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 关键字。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TrueKeyword&lt;/strong&gt;、&lt;strong&gt;FalseKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 关键字。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LockKeyword&lt;/strong&gt;、&lt;strong&gt;CheckedKeyword&lt;/strong&gt;、&lt;strong&gt;UncheckedKeyword&lt;/strong&gt;、&lt;strong&gt;UnsafeKeyword&lt;/strong&gt;、&lt;strong&gt;FixedKeyword&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;checked&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;unchecked&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;fixed&lt;/code&gt; 关键字。&lt;/p&gt;

&lt;h3 id=&quot;符号&quot;&gt;符号&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DotToken&lt;/strong&gt;、&lt;strong&gt;SemicolonToken&lt;/strong&gt;、&lt;strong&gt;OpenBraceToken&lt;/strong&gt;、&lt;strong&gt;CloseBraceToken&lt;/strong&gt;、&lt;strong&gt;LessThanToken&lt;/strong&gt;、&lt;strong&gt;GreaterThanToken&lt;/strong&gt;、&lt;strong&gt;OpenParenToken&lt;/strong&gt;、&lt;strong&gt;CloseParenToken&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是 C# 中的各种符号：&lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;{&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;}&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;(&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;)&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;空白&quot;&gt;空白&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;EndOfLineTrivia&lt;/strong&gt; 表示换行，&lt;strong&gt;WhitespaceTrivia&lt;/strong&gt; 表示空格，&lt;strong&gt;EndOfFileToken&lt;/strong&gt; 表示文件的末尾。&lt;/p&gt;

&lt;p&gt;通常，这两个语法节点会在另一个节点的里面，作为另一个节点的最后一部分。比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;using Walterlv.Demo;&lt;/code&gt; 是一个 UsingDirective，它的最后一个节点 Semicolon 中就会包含换行符 EndOfLineTrivia。&lt;/p&gt;

&lt;h3 id=&quot;指令&quot;&gt;指令&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;UsingDirective&lt;/strong&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt; 指令。一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;using&lt;/code&gt; 指令包含一个 UsingKeyword，一个 QualifiedName 和一个 Semicolon（&lt;code class=&quot;highlighter-rouge&quot;&gt;;&lt;/code&gt;）。&lt;/p&gt;

&lt;h3 id=&quot;声明&quot;&gt;声明&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NamespaceDeclaration&lt;/strong&gt;、&lt;strong&gt;ClassDeclaration&lt;/strong&gt;、&lt;strong&gt;MethodDeclaration&lt;/strong&gt;、&lt;strong&gt;PropertyDeclaration&lt;/strong&gt;、&lt;strong&gt;FieldDeclaration&lt;/strong&gt;、&lt;strong&gt;VariableDeclaration&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;分别是命名空间、类型、方法、属性、。&lt;/p&gt;

&lt;p&gt;其中，属性声明包含一个 &lt;strong&gt;AccessorList&lt;/strong&gt;，即属性访问器列表，访问期列表可以包含 &lt;strong&gt;GetAccessorDeclaration&lt;/strong&gt;（属性 get）、&lt;strong&gt;SetAccessorDeclaration&lt;/strong&gt;（属性 set）的声明。&lt;/p&gt;

&lt;p&gt;这些声明通常是嵌套存在的。例如一个常规的文件的第 0、1 级语法节点通常是这样的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CompilationUnit
    &lt;ul&gt;
      &lt;li&gt;UsingDirective&lt;/li&gt;
      &lt;li&gt;UsingDirective&lt;/li&gt;
      &lt;li&gt;NamespaceDeclaration&lt;/li&gt;
      &lt;li&gt;EndOfFileToken&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;类型声明是命名空间声明的子节点，类型成员的声明是类型声明的子节点。&lt;/p&gt;

&lt;h3 id=&quot;名称和标识符&quot;&gt;名称和标识符&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;QualifiedName&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;限定名称，可以理解为完整的名称。&lt;/li&gt;
      &lt;li&gt;例如命名空间 Walterlv.DemoTool 的限定名称就是这个全称 Walterlv.DemoTool；类型 Walterlv.DemoTool.Foo 的限定名称也是这个全程 Walterlv.DemoTool.Foo。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IdentifierName&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;标识名称，当前上下文下的唯一名称。&lt;/li&gt;
      &lt;li&gt;例如 Walterlv 和 DemoTool 都是 Walterlv.DemoTool 这个命名空间的标识符。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IdentifierToken&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;标识符，具体决定 IdentifierName 的一个字符串。&lt;/li&gt;
      &lt;li&gt;这其实与 IdentifierName 是一样的意思，但是在语法树上的不同节点。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GenericName&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;泛型名称，即 Foo&lt;T&gt; 这种。&lt;/T&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;特性&quot;&gt;特性&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AttributeList&lt;/strong&gt;、&lt;strong&gt;Attribute&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;一个允许添加特性的地方，如果添加了特性，那么可以得到 AttributeList 节点，内部包含了多个 Attribute 子节点。&lt;/p&gt;

&lt;h3 id=&quot;形参和实参&quot;&gt;形参和实参&lt;/h3&gt;

&lt;p&gt;形参是 parameter，实参是 argument。前者是定义的参数，后者是实际传入的参数。&lt;/p&gt;

&lt;p&gt;语法节点中有两种不同的形参和实参，一个是泛型，一个是普通参数。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;ParameterList&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;形参列表，出现在方法声明中，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;void Foo(string a, bool b)&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(string a, bool b)&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Parameter&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;形参，即以上例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;string a&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool b&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ArgumentList&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;实参列表，出现在方法调用中，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;this.Foo(a, b)&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;(a, b)&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Argument&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;实参，即以上例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TypeParameterList&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;泛型形参列表，出现在类型声明或者方法声明中，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;void Foo&amp;lt;T1, T2&amp;gt;(string a)&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;T1, T2&amp;gt;&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TypeParameter&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;泛型形参，即以上例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;T2&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TypeArgumentList&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;泛型实参列表，出现在使用泛型参数的地方，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;this.Foo&amp;lt;T1, T2&amp;gt;()&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;T1, T2&amp;gt;&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TypeArgument&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;泛型实参，即以上例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;T1&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;T2&lt;/code&gt; 部分。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;语句块&quot;&gt;语句块&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Block&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;即用 &lt;code class=&quot;highlighter-rouge&quot;&gt;{&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;}&lt;/code&gt; 包裹的语句代码。&lt;/li&gt;
      &lt;li&gt;当然并不是所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;{&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;}&lt;/code&gt; 包裹的都是语句（例如类型声明就不是），里面真正有代码时才是语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;EqualsValueClause&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;等号子句，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;= null&lt;/code&gt;。我们经常称之为“赋值”语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;语句&quot;&gt;语句&lt;/h3&gt;

&lt;p&gt;一个语句是指包含分号在内的实际执行的句子。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;LocalDeclarationStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;本地变量声明语句，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;var a = 0;&lt;/code&gt; 这样的句子；其中，去掉分号的部分即前面我们提到的变量声明 VariableDeclaration。&lt;/li&gt;
      &lt;li&gt;一个本地变量声明的语句也可以不包含赋值。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ExpressionStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;表达式语句，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;this.Foo();&lt;/code&gt; 这样的一次方法调用。如果去掉分号，剩下的部分是表达式（Expression）。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IfStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;if 语句，即一个完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else if&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ForStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;for 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ForEachStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;for 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;WhileStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;while 语句，即一个完整的 &lt;code class=&quot;highlighter-rouge&quot;&gt;while&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DoStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;do-while 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DefaultStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;default();&lt;/code&gt; 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ReturnStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;return 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CheckedStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;checked 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;UncheckedStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;checked 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;UnsafeStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;unsafe 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FixedStatement&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;unsafe 语句。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;表达式&quot;&gt;表达式&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;EqualsExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;相等判断表达式，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;a == b&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;InvocationExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;调用表达式，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Class.Method(xxx)&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;instance.Method(xxx)&lt;/code&gt; 这种完整的调用。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SimpleMemberAccessExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;这是 InvocationExpression 的子节点，是方法调用除去参数列表的部分，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;Class.Method&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;instance.Method&lt;/code&gt;。&lt;/li&gt;
      &lt;li&gt;如果是获取属性（没有参数列表），那么也是这个节点。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AwaitExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;await 表达式，即 &lt;code class=&quot;highlighter-rouge&quot;&gt;await this.Foo()&lt;/code&gt; 这样的调用。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DefaultExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;default()&lt;/code&gt; 表达式。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TrueLiteralExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 表达式。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FalseLiteralExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 表达式。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ParenthesizedLambdaExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;带括号的 lambda 表达式，例如：&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;() =&amp;gt; xxx&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(a) =&amp;gt; xxx&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(a, b) =&amp;gt; xxx&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(int a, string b) =&amp;gt; xxx&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;() =&amp;gt; { }&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(a) =&amp;gt; { }&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(a, b) =&amp;gt; { }&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;(int a, string b) =&amp;gt; { }&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SimpleLambdaExpression&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;不带括号的 lambda 表达式，例如：&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;a =&amp;gt; xxx&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;a =&amp;gt; { }&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;基元类型&quot;&gt;基元类型&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PredefinedType&lt;/strong&gt; 是所有基元类型的节点。它的子节点可能是 BoolKeyword、StringKeyword 或其它基元类型的关键字。&lt;/p&gt;

&lt;h3 id=&quot;c-内建类型&quot;&gt;C# 内建类型&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NullableType&lt;/strong&gt;、&lt;strong&gt;TupleType&lt;/strong&gt;、&lt;strong&gt;ArrayType&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;这三个分别是 C# 中语法级别支持的类型，分别是可空类型、元组类型和数组类型。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;NullableType
    &lt;ul&gt;
      &lt;li&gt;即 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool?&lt;/code&gt; 这种用于创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;Nullable&amp;lt;bool&amp;gt;&lt;/code&gt; 的语法。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;TupleType
    &lt;ul&gt;
      &lt;li&gt;即 &lt;code class=&quot;highlighter-rouge&quot;&gt;(bool, string)&lt;/code&gt; 这种用于创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;ValueTuple&amp;lt;bool, string&amp;gt;&lt;/code&gt; 的语法。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ArrayType
    &lt;ul&gt;
      &lt;li&gt;即 &lt;code class=&quot;highlighter-rouge&quot;&gt;[]&lt;/code&gt; 这种用于创建数组类型的语法。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 18 Jul 2018 12:24:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/roslyn-syntax-tree-nodes.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/roslyn-syntax-tree-nodes.html</guid>
        
        
        <category>roslyn</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET Standard 的管理策略</title>
        <description>&lt;p&gt;.NET Standard 作为各大 .NET 的标准，我们有必要了解一下它是如何在各种 .NET 的实现之间履行自己的职责的。所以，本文会说说它的管理策略。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;都有哪些-net-standard-的实现&quot;&gt;都有哪些 .NET Standard 的实现？&lt;/h2&gt;

&lt;p&gt;目前 .NET Standard 的实现有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;.NET Core&lt;/li&gt;
  &lt;li&gt;.NET Framework&lt;/li&gt;
  &lt;li&gt;Mono&lt;/li&gt;
  &lt;li&gt;Unity&lt;/li&gt;
  &lt;li&gt;Xamarin&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;标准在前还是实现在前&quot;&gt;标准在前还是实现在前？&lt;/h2&gt;

&lt;p&gt;标准在前指的是先制定出 .NET Standard 的某个版本的标准，然后再由各个 .NET Standard 的实现去完成实现。而实现在前指的是待各个 .NET Standard 的实现完成某个版本的发布之后，.NET Standard 再进行新版本的发布，确保发布时所有实现都已有版本完成。&lt;/p&gt;

&lt;p&gt;.NET Standard 采取的是后者——实现在前。&lt;/p&gt;

&lt;p&gt;主要在于，如果 .NET Standard 的 API 先发布，那么很多开发者基于新 .NET Standard API 开发的应用可能根本就没有办法编译到 .NET 的各个实现，例如 Mono/Xamarin。&lt;/p&gt;

&lt;h2 id=&quot;标准之内还是使用标准&quot;&gt;标准之内还是使用标准？&lt;/h2&gt;

&lt;p&gt;.NET Standard 的发布有两种不同的方式。&lt;/p&gt;

&lt;p&gt;第一种，也是大家经常提及的一种，即要求各大 .NET 实现都内置的 API 集。当我们在项目文件中指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;netstandard&lt;/code&gt; 时，我们可以直接地原生地使用到的那些 API。&lt;/p&gt;

&lt;p&gt;第二种，是通过 NuGet 包发布的基于 .NET Standard 标准实现的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TargetFramework&lt;/code&gt; 指定为 &lt;code class=&quot;highlighter-rouge&quot;&gt;netstandard&lt;/code&gt; 的类库。不止微软通过这种方式发布了大量基于 .NET Standard 的类库，&lt;nuget.org&gt; 上大量流行的库也基本上都有生成基于 `netstandard` 的版本。而这种并不需要各大 .NET 实现对此做额外的发布都能够正常使用，因为这种发布到 NuGet 上的包本身已自带一份实现。&lt;/nuget.org&gt;&lt;/p&gt;

&lt;p&gt;这两种不同的方式分别独立更新而互不影响。&lt;/p&gt;

&lt;h2 id=&quot;并不一定都能实现的标准&quot;&gt;并不一定都能实现的标准&lt;/h2&gt;

&lt;p&gt;.NET Standard 中的 API 并不一定都是能被各大 .NET 的实现来实现的，因为现实的运行环境总是有或多或少的限制。&lt;/p&gt;

&lt;p&gt;典型的例子是——苹果 App Store 的应用商店不允许应用在运行时生成可执行代码，所以 Xamarin 的 iOS 版本就无法实现运行时代码生成的部分标准。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/standard/blob/master/docs/governance/README.md&quot;&gt;standard/README.md at master · dotnet/standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 08 Jul 2018 14:28:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/net-standard-governance.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/net-standard-governance.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>在 Visual Studio 的解决方案资源管理器中隐藏一些文件</title>
        <description>&lt;p&gt;项目文件中有一些属性几乎是专门为 IDE 而准备的，不过考虑到 .NET 生态的开发者多数都使用 Visual Studio，所以基本上也只有 Visual Studio 对这些特性支持的最全面。（才不会透漏这些属性其实本就是为 Visual Studio 而准备的呢。）&lt;/p&gt;

&lt;p&gt;本文将介绍如何在 Visual Studio 的解决方案资源管理器中隐藏一些文件。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;原生支持&quot;&gt;原生支持&lt;/h2&gt;

&lt;p&gt;Visual Studio 原生支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visible&lt;/code&gt; 属性用来控制某一项文件是否在 Visual Studio 的解决方案资源管理器中显示。具体来说，是这样设置的：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EnableDefaultItems&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EnableDefaultItems&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj\**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Visible=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;好了，任务完成，全文结束！&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;要是只有这样，我才不会写这篇文章呢！&lt;/p&gt;

&lt;h2 id=&quot;原生不支持&quot;&gt;原生不支持&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-04-20-08-19.png&quot; alt=&quot;存在文件夹的情况&quot; /&gt;&lt;/p&gt;

&lt;p&gt;考虑一下像上图那样有些文件在文件夹中的情况，然后我们再次设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visible=&quot;false&quot;&lt;/code&gt; 属性：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-04-20-16-46.png&quot; alt=&quot;文件夹竟然还在&quot; /&gt;&lt;/p&gt;

&lt;p&gt;文件夹竟然还在！这是 Visual Studio 的 Bug 吗？&lt;/p&gt;

&lt;p&gt;还真是，至少在 Visual Studio 的项目系统中就有这样的 Issue 处于打开的状态：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/VSProjectSystem/issues/162&quot;&gt;Content Visible=false hides the item, but not the directories in Solution Explorer · Issue #162 · Microsoft/VSProjectSystem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;回复是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Yes this is a known issue. We are discussing options to resolve it over here &lt;a href=&quot;https://github.com/dotnet/project-system/issues/1233&quot;&gt;dotnet/roslyn-project-system#1233&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好吧，那就等着解决吧！不过等大家的 Visual Studio 更新到解决的版本还需要很久吧。&lt;/p&gt;

&lt;h2 id=&quot;变通解决&quot;&gt;变通解决&lt;/h2&gt;

&lt;p&gt;所以，我们只好采取其他手段来解决，最容易想到的是编写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IncludeSourceCodes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bin\**\*.cs;obj\**\*.cs;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，引入这些文件就是通过在编译时才引入的。没有开始编译时，项目中自然看不见。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-07-04-20-29-52.png&quot; alt=&quot;完全看不见了&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果这样的例子发生在制作的 NuGet 包中，那么这个文件可能在 NuGet 包中的路径是 /build/Walterlv.Demo.targets；为了引用额外的源码，我们可以加上额外的路径信息：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IncludeSourceCodes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeforeTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CoreCompile&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\src\**\*.cs&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(MSBuildThisFileDirectory)..\src\bin\**\*.cs;$(MSBuildThisFileDirectory)..\src\obj\**\*.cs;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;活学活用&quot;&gt;活学活用&lt;/h2&gt;

&lt;p&gt;这并不是说在 Visual Studio 的解决方案资源管理器中，隐藏文件都应该采用 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;Target /&amp;gt;&lt;/code&gt; 来做，毕竟这样太复杂了。如果没有太复杂的要求，直接些 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visible=&quot;false&quot;&lt;/code&gt; 也未尝不可。&lt;/p&gt;

&lt;p&gt;比较复杂的情况可能比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-simplest-way-to-pack-a-source-code-nuget-package&quot;&gt;制作跨平台的 NuGet 源码包，安装后就像直接把源码放进项目一样&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;需要额外为项目准备一些辅助运行的必要文件&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/VSProjectSystem/issues/162&quot;&gt;Content Visible=false hides the item, but not the directories in Solution Explorer · Issue #162 · Microsoft/VSProjectSystem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 04 Jul 2018 12:30:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/make-items-invisible-in-vs-solution-explorer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/make-items-invisible-in-vs-solution-explorer.html</guid>
        
        
        <category>msbuild</category>
        
        <category>nuget</category>
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>如何在 MSBuild Target（Exec）中报告编译错误和编译警告</title>
        <description>&lt;p&gt;我曾经写过一篇文章 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt;，通过编写一个控制台程序来参与编译过程。但是，相比于 &lt;a href=&quot;/post/create-a-cross-platform-msbuild-task-based-nuget-tool&quot;&gt;基于 Task 的方式&lt;/a&gt;，可控制的因素还是太少了。&lt;/p&gt;

&lt;p&gt;有没有什么办法能够让控制台程序也能与 MSBuild Target 之间发生更多的信息交换呢？比如报告编译错误和编译警告？答案是有的，通过格式化控制台输出。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;编译错误和编译警告&quot;&gt;编译错误和编译警告&lt;/h2&gt;

&lt;p&gt;MSBuild 的 Exec 自带有错误和警告的标准格式，按照此格式输出，将被识别为编译错误和编译警告。&lt;/p&gt;

&lt;p&gt;而格式只是简简单单的 &lt;code class=&quot;highlighter-rouge&quot;&gt;error:&lt;/code&gt; 开头或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;warning:&lt;/code&gt; 开头。冒号前面也可以加上空格。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;warning: walterlv 最好是一个逗比。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error: walterlv 必须是一个逗比。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于这样一段在编译期间执行的程序，编译时将显示如下信息，并产生编译错误和编译警告。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-20-13-10-34.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，在这个例子中，我直接在编译完成后执行自己，产生了这样的编译错误。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Target&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PostBuild&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AfterTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PostBuildEvent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)$(AssemblyName).exe&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;更复杂的错误和警告控制&quot;&gt;更复杂的错误和警告控制&lt;/h2&gt;

&lt;p&gt;实际上，上面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;warning&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;error&lt;/code&gt; 只是省略的格式，而完整的部分是这样的：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;file_path(line_start,column_start,line_end,column_end): error_or_warning key: message
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;file_path 是文件的绝对路径或相对于项目文件的路径，这样的输出之后在 Visual Studio 中双击之后可以定位到文件。&lt;/li&gt;
  &lt;li&gt;line_start、column_start、line_end、column_end 控制双击之后选中文件的开始和结束行列。&lt;/li&gt;
  &lt;li&gt;error_or_warning 可选为 error 或者 warning。&lt;/li&gt;
  &lt;li&gt;key 是一个唯一标识符，如果用户认为可以忽略这样的错误，则可以使用这个唯一的 key 来禁止某一特定项的警告。&lt;/li&gt;
  &lt;li&gt;message 则是普通的消息提示内容。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Demo.cs(344,59,344,78): warning CS0067: The event 'WalterlvClass.Foo' is never used.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;阻止编译错误和编译警告的格式化识别&quot;&gt;阻止编译错误和编译警告的格式化识别&lt;/h2&gt;

&lt;p&gt;当然，有可能你只是需要一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;error:&lt;/code&gt; 开头或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;warning:&lt;/code&gt; 开头的格式，并不希望真的产生编译错误或者编译警告，那么只需要在执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exec&lt;/code&gt; 的时候设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;IgnoreStandardErrorWarningFormat=&quot;True&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IgnoreStandardErrorWarningFormat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Command=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(OutputPath)$(AssemblyName).exe&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/77eb8b02-8cd7-4d32-acad-3ab0dc308d78/exec-task-and-error-in-output?forum=msbuild&quot;&gt;Exec task and “error :” in output&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 02 Jul 2018 12:49:55 +0000</pubDate>
        <link>https://blog.walterlv.com/post/standard-error-warning-format.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/standard-error-warning-format.html</guid>
        
        
        <category>dotnet</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>语义耦合（Semantic Coupling）</title>
        <description>&lt;p&gt;跟小伙伴一起重构一段 UI，试图将用户界面和业务代码分离的时候，小伙伴试图在业务代码中直接调用 UI。我们当然都知道这会产生耦合，于是小伙伴试图定义一些属性、变量或接口来解决这个耦合。虽然在代码的静态分析中，这一的耦合消失了，但我始终觉得不妥。觉得耦合依然存在，只是不再能被静态分析了。&lt;/p&gt;

&lt;p&gt;我想到一个词——“语义耦合（Semantic Coupling）”，搜索发现也有很多小伙伴在关心这个问题。而且，从他们的文章和讨论中，我也了解到更多关于语义耦合的种类和危害。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;什么是语义耦合&quot;&gt;什么是语义耦合&lt;/h2&gt;

&lt;p&gt;这是区别于常规意义上的“耦合”而言的。&lt;/p&gt;

&lt;p&gt;即类 Foo 依赖于类 Bar，即是常规意义上的耦合。静态代码分析工具就可以为我们发现这种耦合。如果将 Bar 拆开成两个部分，一是类 Bar 的实现本身，另一个是接口 IBar；现在 Foo 依赖的是接口 IBar，那么 Foo 就没有依赖类 Bar了。在静态代码分析工具中就会发现这样的依赖就解除了。&lt;/p&gt;

&lt;p&gt;在静态代码分析工具认为没有耦合的情况之下，如果两个类之间还&lt;strong&gt;交换带有隐含意义的数据&lt;/strong&gt;，&lt;strong&gt;假设对方已为自己完成了某种工作&lt;/strong&gt;，&lt;strong&gt;暗示对方执行期望的代码&lt;/strong&gt;，那么这两个类在语义上还存在着耦合。&lt;/p&gt;

&lt;p&gt;我们说耦合的危害是修改一个类的时候，另一个类也需要做对应的修改。显式耦合有工具帮我们做重构时的解耦，而语义上的耦合却很难有准确帮助我们的工具。一些变态的工具（例如 ReSharper）能够帮助我们解决一部分。&lt;/p&gt;

&lt;h2 id=&quot;哪些代码算作语义耦合&quot;&gt;哪些代码算作语义耦合&lt;/h2&gt;

&lt;p&gt;按照上面的定义，语义耦合的概念依然模糊，但都有一个统一的核心——&lt;strong&gt;在实现细节上存在依赖&lt;/strong&gt;，而不是在调用上存在依赖。&lt;/p&gt;

&lt;h3 id=&quot;交换带有隐含意义的数据&quot;&gt;交换带有隐含意义的数据&lt;/h3&gt;

&lt;p&gt;在这段代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 依赖于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt;，他们都依赖于 &lt;code class=&quot;highlighter-rouge&quot;&gt;FooInfo&lt;/code&gt;。至少静态代码分析工具是这么认为的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 后续代码。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FooInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是，其实这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 也依赖于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt;（反向依赖），因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 总假设 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 一定传了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;FooInfo&lt;/code&gt; 类型的参数。&lt;/p&gt;

&lt;p&gt;在这里，&lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 的隐式依赖就构成了“&lt;strong&gt;语义耦合&lt;/strong&gt;”。&lt;/p&gt;

&lt;p&gt;如何消灭这段语义耦合呢？&lt;/p&gt;

&lt;p&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 类型的参数改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;FooInfo&lt;/code&gt; 类型是一个可选方案。但是，如果此函数是为了实现某个接口，&lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt; 是接口中对应方法的参数类型，那就不能这么改了。此时应该审视是否应该传入这个参数，或者审视接口设计的合理性。&lt;/p&gt;

&lt;h3 id=&quot;假设对方已为自己完成了某种工作&quot;&gt;假设对方已为自己完成了某种工作&lt;/h3&gt;

&lt;p&gt;典型的情况是要求调用某方法前先调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Init&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这段代码中，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 在使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&lt;/code&gt; 方法之前没有调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Init&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 是会抛出异常的（事实上实现代码的异常不应该抛出，详情请参阅我的另一篇文章 &lt;a href=&quot;/post/throws-which-exception.html#%E6%B0%B8%E8%BF%9C%E4%B8%8D%E5%BA%94%E8%AF%A5%E8%AE%A9%E5%AE%9E%E7%8E%B0%E9%94%99%E8%AF%AF%E6%8A%9B%E5%87%BA&quot;&gt;永远不应该让实现异常抛出 - 吕毅&lt;/a&gt;）。类似的情况还有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 中存在必须先赋值才能正常使用的字段/属性，或者必须按照特定的顺序调用才能正常实现的业务。&lt;/p&gt;

&lt;p&gt;这里 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 便产生了对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar&lt;/code&gt; 语义上的耦合。虽然并没有明显的依赖，但几乎所有使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的对象都要求要写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bar.Test()&lt;/code&gt; 里面的实现那样，否则用起来就不正常。&lt;/p&gt;

&lt;p&gt;解决这里的语义耦合倒是有很多方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;Init&lt;/code&gt; 方法，改到构造函数中&lt;/li&gt;
  &lt;li&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Init&lt;/code&gt; 改为普通的别的名称（比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;InitializeXxx&lt;/code&gt;），然后让 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&lt;/code&gt; 方法允许在 &lt;code class=&quot;highlighter-rouge&quot;&gt;_demo&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 时正常工作（并能解释为什么正常）&lt;/li&gt;
  &lt;li&gt;如果初始化非常复杂必须在其他方法中实现，那么需要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&lt;/code&gt; 方法的开头进行状态预判，并抛出异常说明必须先进行初始化（毕竟通过异常报告使用错误是强有力的文档，关于使用错误，请参阅我的另一篇文章 &lt;a href=&quot;/post/throws-which-exception.html#%E4%BD%BF%E7%94%A8%E9%94%99%E8%AF%AF&quot;&gt;使用错误 - 吕毅&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只有去掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;Init&lt;/code&gt; 方法才是真的解决了语义耦合，其他都是缓解语义耦合带来的危害。&lt;/p&gt;

&lt;h3 id=&quot;暗示对方执行期望的代码&quot;&gt;暗示对方执行期望的代码&lt;/h3&gt;

&lt;p&gt;目前主流的 MVVM 框架几乎都支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 机制，为了解决部分情况下 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 的操作需要通知到 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 来完成的情况。&lt;/p&gt;

&lt;p&gt;这是一个好机制，因为它在框架层完成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 消息的传递，避免了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 的依赖。&lt;/p&gt;

&lt;p&gt;但是，这个机制太万能了，以至于各种不同的开发中可能写出实际上依然在耦合的代码（名义上已经不耦合了）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMessageReceiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowErrorInfoMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMessageReceiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeleteAnimationMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnReceived&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowIOErrorInfoMessage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 弹窗显示 IO 错误。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnReceived&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeleteAnimationMessage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 播放某一项数据删除的动画。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ViewModelBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 执行某段业务代码。&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SendMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DeleteAnimationMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;removingItemId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 继续执行某段业务代码。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SendMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShowIOErrorInfoMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 试图向 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 发送播放删除动画的消息和显示错误提示的消息，让 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 来播放动画并显示这些错误。&lt;/p&gt;

&lt;p&gt;如果进行静态代码分析，&lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 依然对 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 没有任何依赖，但它们依然存在&lt;strong&gt;语义耦合&lt;/strong&gt;。因为已经可以通过阅读代码来明白 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 正在试图播放动画和显示错误提示框。&lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt;&lt;strong&gt;正在期望对方来为自己实现某项自己无法单独实现的功能&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 毕竟是 MVVM 框架中一个强大的组成部分，只依赖于此机制也能够部分消除此耦合。方法是将 &lt;code class=&quot;highlighter-rouge&quot;&gt;DeleteAnimationMessage&lt;/code&gt; 改名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemRemovingMessage&lt;/code&gt;，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;ShowIOErrorInfoMessage&lt;/code&gt; 改名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;ErrorOccurredMessage&lt;/code&gt;。如此改动，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 的代码中将不再包含任何期望 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 执行的逻辑，&lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 自己决定删除元素时是否播放动画（还是决定元素变灰），自己决定是否显示错误提示（还是决定自动纠正）。&lt;/p&gt;

&lt;p&gt;这样的改动基本上没有语义耦合了，但我认为依然存在很弱的耦合，因为依然存在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 试图期望 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 做某个任务，只是任务已经非常抽象了。&lt;/p&gt;

&lt;p&gt;我在自己编写的 MVVM 框架中弱化了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Message&lt;/code&gt; 的机制（是非常的弱），逼迫 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 的实现者不要试图通知 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 做任何事情，而是由 &lt;code class=&quot;highlighter-rouge&quot;&gt;View&lt;/code&gt; 的实现者决定是否对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ViewModel&lt;/code&gt; 中任务的执行结果进行反馈。&lt;/p&gt;

&lt;h2 id=&quot;为什么语义耦合也有危害&quot;&gt;为什么语义耦合也有危害&lt;/h2&gt;

&lt;p&gt;直接的耦合可以在静态代码分析工具的帮助下帮助我们理清楚依赖关系并批量重构（重命名等），不过这个过程是非常痛苦的，尤其是耦合是双向的时候，或者被非常多类耦合的时候。&lt;/p&gt;

&lt;p&gt;而语义上的耦合很难被静态代码分析工具分析出来，危害没有直接的耦合那么大，改起来也不那么痛苦。不过也有一些问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;可能会隐藏着某些 BUG（尤其是在修改了被语义耦合的类时，根本就不知道对方会用怎样的方式在语义上耦合自己，改完还不一定出异常）&lt;/li&gt;
  &lt;li&gt;不利于单元测试（语义耦合会使得单元测试的用例变多，但可能根本就是无效或重复的；或者使得某些用例变得不可测，例如上面例子中要求单元测试播放动画或者显示错误提示框是不合理的）&lt;/li&gt;
  &lt;li&gt;设计上不那么好看（至少对强迫症患者来说是这样）&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.michaelnygard.com/blog/2015/04/the-perils-of-semantic-coupling/&quot;&gt;The Perils of Semantic Coupling - Wide Awake Developers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.alejandrodu.com/blog/semantic-coupling&quot;&gt;Semantic coupling in code - Alejandro Duarte&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 30 Jun 2018 07:01:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/semantic-coupling.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/semantic-coupling.html</guid>
        
        
        <category>dotnet</category>
        
        <category>framework</category>
        
      </item>
    
      <item>
        <title>XML 的 XPath 语法</title>
        <description>&lt;p&gt;XPath 是 XML 路径语言（XML Path Language），用来确定XML文档中某部分位置的语言。无论是什么语言什么框架，几乎都可以使用 XPath 来高效查询 XML 文件。&lt;/p&gt;

&lt;p&gt;本文将介绍 XPath 的一些语法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文读写的 XML 文件会以 &lt;a href=&quot;#%E5%81%87%E8%AE%BE%E7%9A%84-xml-%E6%96%87%E4%BB%B6&quot;&gt;文章末尾的代码 - 假设的 XML 文件&lt;/a&gt; 作为示例。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;XPath 被称作 XML 路径语言，正出自于其最重要的 —— 路径表达式。&lt;/p&gt;

&lt;h2 id=&quot;路径&quot;&gt;路径&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/package/metadata/id&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这样的路径描述语法将可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;package&lt;/code&gt; 节点下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;metadata&lt;/code&gt; 节点下的 &lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt; 节点。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/package/metadata/*[1]&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 可以找到任意名称，于是这样的路径描述语法将可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;metadata&lt;/code&gt; 下第一个节点，名称是任意的。&lt;/li&gt;
      &lt;li&gt;尤其要注意的是，XPath 的路径语法第一个节点从 1 开始，而不是 0。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/package//dependency&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;//&lt;/code&gt; 表示只要是前面节点的内部即可，无论中间经过了多少层。&lt;/li&gt;
      &lt;li&gt;如果把 &lt;code class=&quot;highlighter-rouge&quot;&gt;//&lt;/code&gt; 写到了最前面，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;//dependency&lt;/code&gt;，那么表示寻找任意位置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;dependency&lt;/code&gt; 节点。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其实，上面的那些语法都是简写形式的语法，如果将它们完整写出来，将是这样的形式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/child::package/child::metadata/child::id&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/child::package/child::metadata/child::node()[1]&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/child::package/descendant-or-self::dependency&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;child&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;descendant-or-self&lt;/code&gt; 是轴描述语法，除了这两个，还有这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;child
    &lt;ul&gt;
      &lt;li&gt;子节点 &lt;em&gt;可以省略不写&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;attribute
    &lt;ul&gt;
      &lt;li&gt;属性 &lt;em&gt;可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt; 来缩写&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;descendant
    &lt;ul&gt;
      &lt;li&gt;子孙节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;descendant-or-self
    &lt;ul&gt;
      &lt;li&gt;自身引用及子孙节点，可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;//&lt;/code&gt; 来缩写&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;parent
    &lt;ul&gt;
      &lt;li&gt;父节点 &lt;em&gt;可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;..&lt;/code&gt; 来缩写&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ancestor
    &lt;ul&gt;
      &lt;li&gt;祖先节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ancestor-or-self
    &lt;ul&gt;
      &lt;li&gt;自身引用及祖先节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;following
    &lt;ul&gt;
      &lt;li&gt;在此节点后的所有完整节点，即不包含其祖先节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;preceding
    &lt;ul&gt;
      &lt;li&gt;在此节点前的所有完整节点，即不包含其子孙节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;following-sibling
    &lt;ul&gt;
      &lt;li&gt;下一个同级节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;preceding-sibling
    &lt;ul&gt;
      &lt;li&gt;上一个同级节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;self
    &lt;ul&gt;
      &lt;li&gt;自己 &lt;em&gt;可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; 来缩写&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;namespace
    &lt;ul&gt;
      &lt;li&gt;命名空间&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于 attribute 的使用，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;//repository/@type&lt;/code&gt; 查找任意位置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;repository&lt;/code&gt; 节点的 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 属性。&lt;/p&gt;

&lt;h2 id=&quot;节点类型&quot;&gt;节点类型&lt;/h2&gt;

&lt;p&gt;在前面的路径中，我们已经使用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;node()&lt;/code&gt; 来寻找元素节点，除 &lt;code class=&quot;highlighter-rouge&quot;&gt;node()&lt;/code&gt; 表达式之外，还有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;comment()
    &lt;ul&gt;
      &lt;li&gt;注释，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;!-- 注释 --&amp;gt;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;text()
    &lt;ul&gt;
      &lt;li&gt;文字&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;processing-instruction()
    &lt;ul&gt;
      &lt;li&gt;XML 处理指令，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;? 处理指令 ?&amp;gt;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;node()
    &lt;ul&gt;
      &lt;li&gt;节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;节点内容&quot;&gt;节点内容&lt;/h2&gt;

&lt;p&gt;使用中括号来描述节点的内容。&lt;/p&gt;

&lt;p&gt;例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;//repository[@type='git']&lt;/code&gt; 用来查找任意位置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;repository&lt;/code&gt; 节点，并且它有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; 属性值为 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;中括号是可以写多个的，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;//dependency[contains(@exclude, 'Build')][../group/@targetFramework='.NETStandard2.0']/@id&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这将查找所有满足这些条件 &lt;code class=&quot;highlighter-rouge&quot;&gt;dependency&lt;/code&gt; 节点的 &lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt; 属性：
        &lt;ul&gt;
          &lt;li&gt;其 &lt;code class=&quot;highlighter-rouge&quot;&gt;exclude&lt;/code&gt; 属性中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;Build&lt;/code&gt; 字符串&lt;/li&gt;
          &lt;li&gt;其父节点为 &lt;code class=&quot;highlighter-rouge&quot;&gt;group&lt;/code&gt; 且 &lt;code class=&quot;highlighter-rouge&quot;&gt;targetFramework&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;.NETStandard2.0&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;运算符&quot;&gt;运算符&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;//&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;..&lt;/code&gt; 这是前面描述的路径运算符&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; 用于取两个节点查找结果的并集
    &lt;ul&gt;
      &lt;li&gt;例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;//licenseUrl | //projectUrl | //iconUrl&lt;/code&gt; 取任意位置的 &lt;code class=&quot;highlighter-rouge&quot;&gt;licenseUrl&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;projectUrl&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;iconUrl&lt;/code&gt; 节点。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;and&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;对两个条件取“与”或者“或”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;not()&lt;/code&gt; 函数
    &lt;ul&gt;
      &lt;li&gt;对条件取“非”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;div&lt;/code&gt; 以及 &lt;code class=&quot;highlighter-rouge&quot;&gt;mod&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;加减乘除以及取余数&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;!=&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;=&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;比较相等或大小&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多函数&quot;&gt;更多函数&lt;/h2&gt;

&lt;p&gt;w3c 对 XPath 支持的函数有详细的查询页面，可以访问 &lt;a href=&quot;https://www.w3.org/TR/xpath-functions-31/&quot;&gt;XPath and XQuery Functions and Operators 3.1&lt;/a&gt; 查询。&lt;/p&gt;

&lt;h2 id=&quot;在-net-中使用-xpath-语法&quot;&gt;在 .NET 中使用 XPath 语法&lt;/h2&gt;

&lt;p&gt;在 .NET 中使用 XPath 语法可以参考我的另一篇文章：&lt;a href=&quot;/post/read-write-xml-using-xpath-in-dotnet&quot;&gt;.NET 使用 XPath 来读写 XML 文件&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;假设的-xml-文件&quot;&gt;假设的 XML 文件&lt;/h2&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;package&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;metadata&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;MSTestEnhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;authors&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;owners&amp;gt;&lt;/span&gt;walterlv&lt;span class=&quot;nt&quot;&gt;&amp;lt;/owners&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;requireLicenseAcceptance&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/requireLicenseAcceptance&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;licenseUrl&amp;gt;&lt;/span&gt;https://github.com/easiwin/MSTestEnhancer/blob/master/LICENSE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/licenseUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;projectUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/projectUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;iconUrl&amp;gt;&lt;/span&gt;https://easiwin.github.io/mstest-enhancer/icon.png&lt;span class=&quot;nt&quot;&gt;&amp;lt;/iconUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;MSTestEnhancer helps you to write unit tests without naming any method. You can write method contract descriptions instead of writing confusing test method name when writing unit tests.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;releaseNotes&amp;gt;&lt;/span&gt;Support passing null into WithArgument method.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/releaseNotes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;Copyright (c) 2018 dotnet职业技术学院&lt;span class=&quot;nt&quot;&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;repository&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/easiwin/MSTestEnhancer.git&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.ValueTuple&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.4.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETFramework4.7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;group&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.NETStandard2.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTest.TestFramework&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build,Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/metadata&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/XPath&quot;&gt;XPath - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 Jun 2018 11:43:39 +0000</pubDate>
        <link>https://blog.walterlv.com/post/xml-xpath.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/xml-xpath.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>UWP 将图片裁剪成圆形（椭圆）</title>
        <description>&lt;p&gt;不知从什么时候开始，头像流行使用圆形了，于是各个平台开始追逐显示圆形裁剪图像的技术。UWP 有内建的机制支持这种圆形图像裁剪，不过，仅限于画刷。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;WPF 的圆形裁剪请左转参考&lt;/em&gt;：&lt;a href=&quot;/post/clip-wpf-uielement-to-ellipse&quot;&gt;WPF 中使用附加属性，将任意 UI 元素或控件裁剪成圆形（椭圆）&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;与 WPF 不同，UWP 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement.Clip&lt;/code&gt; 属性是 &lt;code class=&quot;highlighter-rouge&quot;&gt;RectangleGeometry&lt;/code&gt; 类型的，这意味着利用此属性是没有办法完成圆形裁剪的。&lt;/p&gt;

&lt;p&gt;但是，存在一个与 WPF 一样的简单一些的方案，直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ImageBrush&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;512&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;512&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Ellipse.Fill&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImageBrush&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ImageSource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Conan_C2.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Ellipse.Fill&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Ellipse&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-15-21-19-44.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是我的头像，原图是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-06-15-21-20-36.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 15 Jun 2018 13:21:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/clip-uwp-image-to-ellipse.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/clip-uwp-image-to-ellipse.html</guid>
        
        
        <category>xaml</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>C#/.NET 中推荐的 Dispose 模式的实现</title>
        <description>&lt;p&gt;如果你觉得你的类需要实现 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt; 接口，还是需要注意一些坑的。不过前人准备了 &lt;strong&gt;Dispose 模式&lt;/strong&gt; 供我们参考，最大程度避免这样的坑。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;C#程序中的 Dispose 方法，一旦被调用了该方法的对象，虽然还没有垃圾回收，但实际上已经不能再使用了。所以使用上要仔细考虑细节。&lt;/p&gt;

&lt;p&gt;需要明确一下 C# 程序（或者说 .NET）中的资源。简单的说来，C# 中的每一个类型都代表一种资源，而资源又分为两类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;托管资源：由 CLR 管理分配和释放的资源，即由 CLR 里 new 出来的对象；&lt;/li&gt;
  &lt;li&gt;非托管资源：不受 CLR 管理的对象，Windows 内核对象，如文件、数据库连接、套接字、COM 对象等；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;毫无例外地，如果我们的类型使用到了非托管资源，或者需要显式释放的托管资源，那么，就需要让类型继承接口 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt;。这相当于是告诉调用者，该类型是需要显式释放资源的，你需要调用我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;不过，这一切并不这么简单，一个标准的继承了 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt; 接口的类型应该像下面这样去实现。这种实现我们称之为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 模式：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DisposableObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDisposable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 获取或设置一个值。该值指示资源已经被释放。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_disposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 执行与释放或重置非托管资源相关的应用程序定义的任务。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SuppressFinalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 关闭此对象使用的所有资源。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 由终结器调用以释放资源。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DisposableObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 执行与释放或重置非托管资源相关的应用程序定义的任务。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// 派生类中重写此方法时，需要释放派生类中额外使用的资源。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disposing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_disposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disposing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 清理托管资源&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// if (managedResource != null)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// {&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     managedResource.Dispose();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     managedResource = null;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// }&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 清理非托管资源&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// if (nativeResource != IntPtr.Zero)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// {&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     Marshal.FreeHGlobal(nativeResource);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     nativeResource = IntPtr.Zero;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// }&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 标记已经被释放。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_disposed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 13 Jun 2018 03:02:37 +0000</pubDate>
        <link>https://blog.walterlv.com/post/recommended-dispose-implementation.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/recommended-dispose-implementation.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码</title>
        <description>&lt;p&gt;Roslyn 是微软为 C# 设计的一套分析器，它具有很强的扩展性。以至于我们只需要编写很少量的代码便能够编译并执行我们的代码。&lt;/p&gt;

&lt;p&gt;作为 Roslyn 入门篇文章之一，你将可以通过本文学习如何开始编写一个 Roslyn 扩展项目 —— 编译一个类，然后执行其中的一段代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是 Roslyn 入门系列之一：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码（本文）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;我们希望做什么&quot;&gt;我们希望做什么？&lt;/h2&gt;

&lt;p&gt;是否有过在编译期间修改一段代码的想法呢？&lt;/p&gt;

&lt;p&gt;我曾经在 &lt;a href=&quot;/post/generate-code-of-generic-types&quot;&gt;生成代码，从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型&lt;/a&gt; 一文中提到过这样的想法，在这篇文章中，我希望只编写泛型的一个参数的版本 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&amp;lt;T&amp;gt;&lt;/code&gt;，然后自动生成 2~16 个参数的版本 &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&amp;lt;T1, T2&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&amp;lt;T1, T2, T3&amp;gt;&lt;/code&gt; … &lt;code class=&quot;highlighter-rouge&quot;&gt;Demo&amp;lt;T1, T2, ... T16&amp;gt;&lt;/code&gt;。不过，在那篇文章中，我写了一个应用程序来完成这样的事情。我在另一篇文章 &lt;a href=&quot;/post/create-a-cross-platform-command-based-nuget-tool&quot;&gt;如何创建一个基于命令行工具的跨平台的 NuGet 工具包&lt;/a&gt; 中说到我们可以将这样的应用程序打包成一个 NuGet 工具包。也就是说，利用这两种不同的技术，我们可以制作一个在编译期间生成多个泛型的 NuGet 工具包。&lt;/p&gt;

&lt;p&gt;不过，这样的生成方式不够通用。今天我们想生成泛型，明天我们想生成多语言类，后天我们又想生成代理类。能否做一种通用的方式来完成这样的任务呢？&lt;/p&gt;

&lt;p&gt;于是，我想到可以使用 Roslyn。在项目中编写一段转换代码，我们使用通用的方式去编译和执行这段代码，以便完成各种各样日益增加的类型转换需求。具体来说，就是 &lt;strong&gt;使用 Roslyn 编译一段代码，然后执行它&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;与之前在 &lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt; 中的不同，我们这次无需打开解决方案或者项目，而是直接寻找并编译源代码文件。所以（利好消息），我们这回可以使用 .NET Core 跨平台版本的 Roslyn 了。所以为了充分有跨平台特性，我们创建&lt;code class=&quot;highlighter-rouge&quot;&gt;控制台应用 (.NET Core)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-25-20-17-01.png&quot; alt=&quot;新建项目&quot; /&gt;&lt;br /&gt;
▲ 千万不要吐槽相比于上一个入门教程来说，这次的界面变成了英文&lt;/p&gt;

&lt;h2 id=&quot;安装必要的-nuget-包&quot;&gt;安装必要的 NuGet 包&lt;/h2&gt;

&lt;p&gt;这次不需要完整的 .NET Framework 环境，也不需要打开解决方案和项目这种重型 API，所以一个简单的 NuGet 包足矣：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-25-20-25-10.png&quot; alt=&quot;安装 Microsoft.CodeAnalysis.CSharp&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;准备一份用于编译和执行代码文件&quot;&gt;准备一份用于编译和执行代码文件&lt;/h2&gt;

&lt;p&gt;我直接使用 &lt;a href=&quot;/post/generate-code-of-generic-types&quot;&gt;生成代码，从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型&lt;/a&gt; 这篇文章中的例子。把其中最关键的文件拿来用于编译和生成试验。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Roslyn&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GenericGenerator&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;@&quot;[System.CodeDom.Compiler.GeneratedCode(&quot;&quot;walterlv&quot;&quot;, &quot;&quot;1.0&quot;&quot;)]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCode&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 替换泛型。&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;out T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;{0}&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;out T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Task&amp;lt;T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Task&amp;lt;({0})&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Func&amp;lt;T, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Func&amp;lt;{0}, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; T, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; {0}, Task&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T, bool&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0}, bool&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var (t, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var ({0}, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, {0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;return (t, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;return ({0}, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;T&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;{0}&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T value)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(({0}) value)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(T t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n} t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(t)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;({0})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var t =&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;var ({0}) =&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; T &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; ({0}) &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;T{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; t;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; ({0});&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 生成 [GeneratedCode]。&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public interface &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public interface &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;    public sealed class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public sealed class &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seperator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{n}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这份代码你甚至可以直接复制到你的项目中，一定是可以编译通过的。&lt;/p&gt;

&lt;h2 id=&quot;编译这份代码&quot;&gt;编译这份代码&lt;/h2&gt;

&lt;p&gt;使用 Roslyn 编译一份代码是非常轻松愉快的。写出以下这三行就够了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;那份代码的全文内容&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compilation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpCompilation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;assemblyname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CSharpCompilationOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutputKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DynamicallyLinkedLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compilation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;好吧，其实我是开玩笑的，这三行代码确实能够跑通过，不过得到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;result&lt;/code&gt; 是编译不通过的结局。为了能够在多数情况下编译通过，我写了更多的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Reflection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Roslyn&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 大家都知道在代码中写死文件路径是不对的，不过，我们这里是试验。放心，我会改的！&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;D:\Development\Demo\Walterlv.Demo.Roslyn\Walterlv.Demo.Roslyn.Tests\GenericGenerator.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompileType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GenericGenerator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 于是我们得到了编译后的类型，但是还不知道怎么办。&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompileType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxTree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 指定编译选项。&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compilation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpCompilation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CSharpCompilationOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutputKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DynamicallyLinkedLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 这算是偷懒了吗？我把 .NET Core 运行时用到的那些引用都加入到引用了。&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 加入引用是必要的，不然连 object 类型都是没有的，肯定编译不通过。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;AppDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAssemblies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MetadataReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateFromFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 编译到内存流中。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MemoryStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compilation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeekOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CompilingException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;执行编译后的代码&quot;&gt;执行编译后的代码&lt;/h2&gt;

&lt;p&gt;既然得到了类型，那么执行这份代码其实毫无压力，因为我们都懂得反射（好吧，我假装你懂反射）。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transform&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;某个泛型类的全文，假装我是泛型类 Walterlv&amp;lt;T&amp;gt; is a sb.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行完之后，里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv&amp;lt;T&amp;gt;&lt;/code&gt; 真的变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16&amp;gt;&lt;/code&gt; 啊。说明成功执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-25-21-14-40.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;下面进入高阶模式&quot;&gt;下面进入高阶模式&lt;/h2&gt;

&lt;p&gt;作为入门篇，我才不会进入高阶模式呢！如果你想实现如本文开头所说的更通用的效果，欢迎发动你的大脑让想象力迸发。当然，如果你确实想不出来，欢迎在下方评论，我将尽快回复。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn&quot;&gt;Compiling C# Code Into Memory and Executing It with Roslyn - Tugberk Ugurlu’s Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 02 Jun 2018 01:26:33 +0000</pubDate>
        <link>https://blog.walterlv.com/post/compile-and-invoke-code-using-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/compile-and-invoke-code-using-roslyn.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码</title>
        <description>&lt;p&gt;Roslyn 是微软为 C# 设计的一套分析器，它具有很强的扩展性。以至于我们只需要编写很少量的代码便能够分析我们的项目文件。&lt;/p&gt;

&lt;p&gt;作为 Roslyn 入门篇文章，你将可以通过本文学习如何开始编写一个 Roslyn 扩展项目，如何开始分析一个解决方案（.sln）中项目（.csproj）的代码文件（.cs）。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文是 Roslyn 入门系列之一：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码（本文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你希望真实地静态分析一个实际项目，并且理解这样的分析过程是如何进行的（而不只是写个 demo），那么本文的所有内容都将是必要的。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;为了能够进行后面关键的操作，我们需要先有一个能跑起来的项目。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-18-18-51-26.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 在 Visual Studio 新建项目，选择“控制台程序(.NET Framework)”&lt;/p&gt;

&lt;p&gt;在目前（2018-06-02 01:26），如果我们需要像本文一样分析现有的解决方案和项目，那么 &lt;strong&gt;.NET Framework 是必须的&lt;/strong&gt;；如果只是分析单个文件，那么也可以选择 .NET Core，参见 &lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;当然，如果你有一个现成的 .NET Core 项目，可以通过修改 .csproj 文件改成 .NET Framework 的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-18-18-57-00.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 从 netcoreapp2.0 改成 net471，因为 NuGet 包中的 ValueTuple 与 net47 不兼容，所以只能选择 net471 或以上  --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net471&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们有了一个可以开始写代码的 Program.cs 文件，接下来就可以正式开始入门了。&lt;/p&gt;

&lt;h2 id=&quot;安装必要的-nuget-包&quot;&gt;安装必要的 NuGet 包&lt;/h2&gt;

&lt;p&gt;在 NuGet 包管理器中搜索并安装 Microsoft.CodeAnalysis 包 —— 这是一个包含 Roslyn 所有 API 的各种 NuGet 包的合集。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-18-19-00-19.png&quot; alt=&quot;Microsoft.CodeAnalysis&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，如果你只是做一些特定的事情，当然不需要安装这么全的 NuGet 包，像 &lt;a href=&quot;https://blog.lindexi.com/post/Roslyn-%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90.html&quot;&gt;Roslyn 静态分析 - 林德熙&lt;/a&gt; 的 demo 和 &lt;a href=&quot;/post/compile-and-invoke-code-using-roslyn&quot;&gt;Roslyn 编译与执行 - 吕毅&lt;/a&gt; 中的教程就不需要安装所有 NuGet 包。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;特别注意&lt;/strong&gt;！！！如果前面你是通过 .NET Core 项目改过来的，那么&lt;strong&gt;还需要额外安装以下三个 NuGet 包，否则运行时会无法打开解决方案和项目&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Build&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Build.Tasks.Core&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Threading.Tasks.Dataflow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;打开一个解决方案项目和其中的文件&quot;&gt;打开一个解决方案/项目和其中的文件&lt;/h2&gt;

&lt;p&gt;现在，我们使用这些代码打开解决方案。我以 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer/&quot;&gt;MSTestEnhancer&lt;/a&gt; 为例：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 打开 MSTestEnhancer(https://github.com/dotnet-campus/MSTestEnhancer/) 解决方案文件。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 注意这里的 MSBuildWorkspace.Create() 会返回 WorkSpace 的实例。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 虽然 WorkSpace 是跨平台的，但是 MSBuildWorkspace 仅在 Windows 下可用。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MSBuildWorkspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OpenSolutionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;@&quot;D:\Developments\Open\MSTestEnhancer\MSTest.Extensions.sln&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
&lt;span class=&quot;c1&quot;&gt;// 从解决方案中选出 MSTest.Extensions 项目。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Projects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MSTest.Extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 从 MSTest.Extensions 项目中选出我们要分析的 ContractTestContext.cs 文件。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里只是一个示例，所以我们只分析一个文件。你可以从 Documents 集合中找出这个项目的所有文件进行分析。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContractTestContext.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;分析代码&quot;&gt;分析代码&lt;/h2&gt;

&lt;p&gt;我们要分析的代码大致是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 这里是 using，省略。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这里是命名空间，省略。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ContractTestContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这是代码的细节，省略。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们开始使用 Roslyn API 找出里面的泛型 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这里，我们必须引入一个概念 —— Syntax Rewriter。&lt;/p&gt;

&lt;h3 id=&quot;语法重写syntax-rewriter&quot;&gt;语法重写——Syntax Rewriter&lt;/h3&gt;

&lt;p&gt;Roslyn 对 C# 代码进行分析的一个非常关键的 API 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt;——这是一个专门用来给你继承的类。&lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt; 是&lt;strong&gt;访问者模式&lt;/strong&gt;中访问者的一个实现，如果你不了解访问者模式，推荐阅读 &lt;a href=&quot;http://blog.csdn.net/zhengzhb/article/details/7489639#reply&quot;&gt;23种设计模式（9）：访问者模式 - CSDN博客&lt;/a&gt; 进行了解，否则我们后面的代码你将只能跟着我写，而不能明白其中的含义。&lt;/p&gt;

&lt;p&gt;当你阅读到这里时，我开始假设你已经了解了访问者模式了。&lt;/p&gt;

&lt;p&gt;我们每个人都可能会写出不同的基于 Roslyn 的分析器，这些分析器通常都会对不同文件的 C# 语法树进行不同的操作；于是，我们通过重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt; 可以实现各种各样不同的操作。在访问者模式中，由于 C# 的语法在一个 C# 版本发布之后就会确定，其中各种各样类型的语法对应访问者模式中的各种不同类型的数据，Roslyn 为我们构建的语法树对应访问者模式中需要访问的庞大的数据结构。由于 Roslyn 的语法树是非常庞大的，以至于对其进行遍历也是一个非常复杂的操作；所以 Roslyn 通过访问者模式为我们封装了这种复杂的遍历过程，我们只需要重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt; 就可以实现对某种特定语法节点的操作。&lt;/p&gt;

&lt;p&gt;现在，我们编写一个用于找出泛型参数 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 的 Syntax Rewriter。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeParameterVisitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxRewriter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisitTypeParameterList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterListSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GreaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其实这段代码就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt; 基类中的代码，我把它贴出来可以帮助我们理解它。&lt;strong&gt;你也依然需要将他放入到我们的项目中&lt;/strong&gt;，因为我们接下来的代码就开始要使用它了。&lt;/p&gt;

&lt;p&gt;如果你想了解更多语法节点，推荐另一篇入门文章：&lt;a href=&quot;/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;访问泛型参数&quot;&gt;访问泛型参数&lt;/h3&gt;

&lt;p&gt;现在，我们继续在之前打开解决方案和项目文件的代码后面增添代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 从我们一开始打开的项目文件中获取语法树。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxTreeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCompilationUnitRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 使用我们刚刚重写 CSharpSyntaxRewriter 的类来访问语法树。&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TypeParameterVisitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 得到的 node 是新的语法树节点，&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 如果我们在 `TypeParameterVisitor` 中修改了语法树，&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 那么这里就会得到修改后的 node 节点。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 我们可以通过这个 node 节点做各种后续的操作。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果我们使用 node 的方式是修改代码，那么可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;var text = node.GetText();&lt;/code&gt; 来得到新的语法树生成的代码，使用这段文本替换之前的文本可以达到修改代码的目的。不过，这不是本文的重点，本文的重点依然在入门。&lt;/p&gt;

&lt;p&gt;现在，整合以上的三大段代码，你的项目应该能够完整地跑起来了。哪三段？1. 打开项目文件；2. &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeParameterVisitor&lt;/code&gt;；3. 访问泛型参数。其中 1 和 3 写在一个方法中，2 是一个新类。&lt;/p&gt;

&lt;h3 id=&quot;分析这个泛型参数&quot;&gt;分析这个泛型参数&lt;/h3&gt;

&lt;p&gt;直到现在，我们所写的任何代码都还只是为了使使用 Roslyn API 的代码能够跑起来，没有进行任何实质上的分析。接下来，我们会修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;CSharpSyntaxRewriter&lt;/code&gt; 以进行真正的分析。不过在此之前，我假设上面的代码你是能正常跑起来而且没有错误的。（&lt;em&gt;如果不行，就在下面留言吧！留言有邮件通知的，我会在第一时间回复你。&lt;/em&gt;）&lt;/p&gt;

&lt;p&gt;如果你不了解 Roslyn，强烈建议去 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisitTypeParameterList&lt;/code&gt; 重写方法中打一个断点观察 &lt;code class=&quot;highlighter-rouge&quot;&gt;lessThanToken&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;parameters&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;greaterThanToken&lt;/code&gt; 这几个实例的含义。&lt;code class=&quot;highlighter-rouge&quot;&gt;lessThanToken&lt;/code&gt; 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;greaterThanToken&lt;/code&gt; 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;parameters&lt;/code&gt; 是一个泛型参数列表，在这里，是一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;现在，我们构造一个自己的泛型参数列表试试，名字不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;T&lt;/code&gt; 了，而是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TParameter&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeparatedSyntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TypeParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TParameter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;特别注意：&lt;code class=&quot;highlighter-rouge&quot;&gt;SeparatedSyntaxList&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 操作&lt;strong&gt;不会&lt;/strong&gt;修改原集合，而是会返回一个新的集合！所以上面 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 之后的赋值语句不能少！这样的设计应该是为了避免遍历语法树的时候语法树被修改导致遍历不可控。&lt;/p&gt;

&lt;p&gt;于是，我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeParameterVisitor&lt;/code&gt; 变成了这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeParameterVisitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxRewriter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisitTypeParameterList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterListSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 构造一个自己的泛型列表，名字改为了 TParameter。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeparatedSyntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TypeParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TParameter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 依然保留之前的更新语法节点的方法。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这样，我们将会在语法树访问结束后得到新的语法树。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GreaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;我们总共编写了两个关键类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Program
    &lt;ul&gt;
      &lt;li&gt;Main（用于打开项目和文件，并调用 TypeParameterVisitor 遍历语法树）&lt;br /&gt;
  &lt;em&gt;需要注意，Main 函数只有 C#7.2 及以上才支持 &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;，如果没有这么高，需要再编写一个新函数，然后在 Main 里面调用它。&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;TypeParameterVisitor
    &lt;ul&gt;
      &lt;li&gt;VisitTypeParameterList（用于遍历和修改语法树中的泛型参数列表）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上便是分析和修改 Roslyn 语法树的简单实例了，我将整个 Program.cs 文件贴在下面，以便整体查看。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.CSharp.Syntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.CodeAnalysis.MSBuild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Demo.Roslyn&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MSBuildWorkspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OpenSolutionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;@&quot;D:\Developments\Open\MSTestEnhancer\MSTest.Extensions.sln&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Projects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MSTest.Extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ContractTestContext.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSyntaxTreeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCompilationUnitRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TypeParameterVisitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeParameterVisitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpSyntaxRewriter&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SyntaxNode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VisitTypeParameterList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterListSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeparatedSyntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeParameterSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;syntaxList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TypeParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TParameter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VisitToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GreaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lessThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greaterThanToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/zhengzhb/article/details/7489639#reply&quot;&gt;23种设计模式（9）：访问者模式 - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://johnkoerner.com/csharp/using-a-csharp-syntax-rewriter/&quot;&gt;John Koerner - Using a CSharp Syntax Rewriter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://joshvarty.com/2014/08/15/learn-roslyn-now-part-5-csharpsyntaxrewriter/&quot;&gt;Learn Roslyn Now: Part 5 CSharpSyntaxRewriter – Shotgun Debugging&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tomassetti.me/code-generation-with-roslyn-a-skeleton-class-from-uml/&quot;&gt;Code Generation with Roslyn: a Skeleton Class from UML - Federico Tomassetti - Software Architect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 02 Jun 2018 01:26:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/analysis-code-of-existed-projects-using-roslyn.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/analysis-code-of-existed-projects-using-roslyn.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
        <category>roslyn</category>
        
      </item>
    
      <item>
        <title>文件被占用？系统自带的“资源监视器(resmon)”也能帮你找到占用它的真凶</title>
        <description>&lt;p&gt;文件或文件夹被占用，然后无法删除？这真的很恼人。关键是还不知道究竟是哪个程序占用的，想退出都不行。&lt;/p&gt;

&lt;p&gt;有各种工具解决此问题，平时下载屯着他们能够省下不少事儿。如果突然间情况非常紧急怎么办？学会使用本文介绍的系统自带工具“资源监视器”，也能立即着手找到真凶！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;删除拒绝提示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-21-11-35.png&quot; alt=&quot;文件夹访问被拒绝 1&quot; /&gt;&lt;br /&gt;
▲ 文件夹访问被拒绝 1&lt;/p&gt;

&lt;p&gt;即便点了继续，依然拒绝：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-21-20-10.png&quot; alt=&quot;文件夹访问被拒绝 2&quot; /&gt;&lt;br /&gt;
▲ 文件夹访问被拒绝 2&lt;/p&gt;

&lt;p&gt;现在，去搜索框（小娜）中搜索“资源监视器”。（如果小娜已经挂掉了，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;resmon&lt;/code&gt; 命令打开。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-21-22-07.png&quot; alt=&quot;启动资源监视器&quot; /&gt;&lt;br /&gt;
▲ 启动资源监视器&lt;/p&gt;

&lt;p&gt;然后，在“关联的句柄”中搜索被占用文件或文件夹的名称。如果名称太通用会搜到太多，所以可以像我这样加上一部分路径。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-21-23-44.png&quot; alt=&quot;搜索关联的句柄&quot; /&gt;&lt;br /&gt;
▲ 搜索关联的句柄&lt;/p&gt;

&lt;p&gt;现在，你就能得到占用这个文件夹的程序了，右击可以结束进程。不过我习惯于正常退出（毕竟这种方式帮助我找到我到底哪个程序忘记关掉了）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-21-25-38.png&quot; alt=&quot;搜索到的进程&quot; /&gt;&lt;br /&gt;
▲ 搜索到的进程&lt;/p&gt;

&lt;p&gt;感谢评论区 &lt;a href=&quot;http://jgrass.cc/&quot;&gt;@晒太阳的猫&lt;/a&gt; 推荐的界面和“资源监视器”一样丑不拉叽但功能强大的 &lt;a href=&quot;http://lockhunter.com/&quot;&gt;LockHunter&lt;/a&gt; 和 &lt;a href=&quot;https://lindexi.github.io/lindexi/&quot;&gt;@林德熙&lt;/a&gt; 推荐的软媒家两年没更新的十八般武器 &lt;a href=&quot;http://mofang.ruanmei.com/&quot;&gt;软媒魔方 - 文件解除&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sun, 27 May 2018 01:02:27 +0000</pubDate>
        <link>https://blog.walterlv.com/post/find-out-which-process-is-using-a-file.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/find-out-which-process-is-using-a-file.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>神器如 dnSpy，无需源码也能修改 .NET 程序</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/0xd4d/dnSpy&quot;&gt;dnSpy&lt;/a&gt; 是 &lt;a href=&quot;https://github.com/0xd4d&quot;&gt;0xd4d&lt;/a&gt; 开发的 .NET 程序调试神器。&lt;/p&gt;

&lt;p&gt;说它是神器真的毫不为过！它能在完全没有源码的情况下即时调试程序，甚至还能修改程序！本文讲向大家介绍如何使用 dnSpy 修改 .NET 程序。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;dnSpy 的主打功能是无需源码的调试，&lt;a href=&quot;https://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 有一篇文章 &lt;a href=&quot;https://blog.lindexi.com/post/%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95-Windows-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;断点调试 Windows 源代码&lt;/a&gt; 介绍了这个方法。而本文主要说其另一项强大的功能 —— 修改程序集。&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;看看-dnspy&quot;&gt;看看 dnSpy&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-45-11.png&quot; alt=&quot;dnSpy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;dnSpy 长着一身 Visual Studio 一样的外观，调试的时候给你熟悉的感觉。&lt;/p&gt;

&lt;p&gt;我们只需要讲我们需要调试或修改的程序集拖入左侧的程序集列表中即可（它会自动为我们把此程序集依赖的程序集也添加进来）。我把以前我写过的一个程序 &lt;code class=&quot;highlighter-rouge&quot;&gt;ManipulationDemo&lt;/code&gt; 拖进来了。&lt;/p&gt;

&lt;h2 id=&quot;实操修改程序集&quot;&gt;实操修改程序集&lt;/h2&gt;

&lt;p&gt;现在我们来修改它，修改什么好呢？为了让效果明显一点，我决定在启动时弹一个窗口。于是我们展开进入到 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; 类中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-48-55.png&quot; alt=&quot;App 类&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后在类中右键“Edit class (C#)”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-50-05.png&quot; alt=&quot;右键 -&amp;gt; 编辑类&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在里面重写 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnStartup&lt;/code&gt; 方法。发现，它竟然连智能感知提示都做了！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-51-37.png&quot; alt=&quot;重写 OnStartup 方法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-52-52.png&quot; alt=&quot;MessageBox.Show&quot; /&gt;&lt;/p&gt;

&lt;p&gt;改完只需要点击一下右下角的编译，即可讲修改应用到我们刚刚打开的程序集中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-53-43.png&quot; alt=&quot;编译&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;保存修改的程序集&quot;&gt;保存修改的程序集&lt;/h2&gt;

&lt;p&gt;如果只是修改了可以立刻运行，那么充其量只是可以辅助调试。但是 dnSpy 是可以将程序集另存到本地的。&lt;/p&gt;

&lt;p&gt;点击“File”-&amp;gt;“Save Module”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-56-00.png&quot; alt=&quot;保存模块&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了以示区分，我写了一个新的名字：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-56-51.png&quot; alt=&quot;保存&quot; /&gt;&lt;/p&gt;

&lt;p&gt;保存完之后，运行：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-22-21-59-53.png&quot; alt=&quot;运行&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们会发现，我们刚刚新增的对话框已经弹出来了。“OK”之后原来的窗口才会显示出来。&lt;/p&gt;

&lt;h2 id=&quot;发挥想象力的时候到了&quot;&gt;发挥想象力的时候到了&lt;/h2&gt;

&lt;p&gt;既然有如此简单的修改程序集的方法，那么我们可以用来做什么事儿呢？用来做什么事儿呢？做什么事儿呢？什么事儿呢？事儿呢？呢？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;想象力&lt;/strong&gt;&lt;em&gt;时间&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;顺便说一下，就算程序集被混淆了也难不倒它。&lt;/p&gt;
</description>
        <pubDate>Tue, 22 May 2018 14:02:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/edit-and-recompile-assembly-using-dnspy.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/edit-and-recompile-assembly-using-dnspy.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>推荐近乎免费的调试神器——OzCode</title>
        <description>&lt;p&gt;当一只断点打在 Visual Studio 的代码编辑器中，程序命中断点的那一刻，调试才刚刚开始……这个时候忙碌的手在键盘和鼠标之间来回跳跃，试图抓住每一次单步执行带来的状态改变。&lt;/p&gt;

&lt;p&gt;如果命中断点的那一刻多数我需要的状态都自动呈现，偶尔需要的状态能够快速定位，那该多好！于是，有了 OzCode……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;OzCode 的官网在这里：&lt;a href=&quot;https://www.oz-code.com/&quot;&gt;OzCode: Innovative debugging extension for Visual Studio&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/EcsxK01G1bw&quot; frameborder=&quot;0&quot; allow=&quot;encrypted-media&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;OzCode 有这些非常吸引我的地方：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当程序进入断点的时候，OzCode 会用红黄绿三色指示程序即将进入的分支&lt;/li&gt;
  &lt;li&gt;OzCode 会在每一个局部变量上方标注它现在的值（不过这一功能 Visual Studio 15.5 开始也提供了）&lt;/li&gt;
  &lt;li&gt;调试 UI 对象时，常常的属性列表在 OzCode 的帮助之下可以快速搜索&lt;/li&gt;
  &lt;li&gt;长长的 linq 语句可以利用 OzCode 看到集合中的每一项对结果的影响（通过滚轮查看）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-18-08-37-56.png&quot; alt=&quot;分支着色&quot; /&gt;&lt;br /&gt;
▲ 分支着色（图片来源于官网）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-18-08-40-44.png&quot; alt=&quot;搜索属性&quot; /&gt;&lt;br /&gt;
▲ 搜索属性（图片来源于官网）&lt;/p&gt;

&lt;p&gt;官网下载的时候会看到提示——&lt;strong&gt;一个月免费试用&lt;/strong&gt;。但事实上，每次 Visual Studio 更新，OzCode 都会重置试用天数。事实上 Visual Studio 2017 开始，更新间隔基本上都在一个月以内。也就是说——&lt;strong&gt;只要勤更新 VS，OzCode 几乎一直免费&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-10-09-05-27.png&quot; alt=&quot;近乎免费&quot; /&gt;&lt;br /&gt;
▲ 每次更新 Visual Studio 之后，OzCode 都会重置&lt;/p&gt;
</description>
        <pubDate>Tue, 22 May 2018 01:47:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/using-ozcode-to-improve-debug.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/using-ozcode-to-improve-debug.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>Grid 布局算法！自己动手实现一个 Grid</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia&quot;&gt;Avalonia&lt;/a&gt; 是一款尚在开发中的基于 .NET Core 的跨平台 UI 框架。目前用在个人项目中还是不错的，不过还需要大家在开源社区中多多支持。&lt;/p&gt;

&lt;p&gt;我为它写了一个全新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局算法，此算法是 WPF 在通常情况下的性能的两倍。本文将分享我在此项目中实现的算法的原理。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;grid-的布局行为到底是怎样的&quot;&gt;Grid 的布局行为到底是怎样的？&lt;/h2&gt;

&lt;p&gt;Grid 算是 WPF/UWP 入门中非常重要的一个布局容器了。面对它那强大而熟悉的布局方式，大家应该没有什么疑问吧！&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;可以定义行和列&lt;/li&gt;
  &lt;li&gt;可以分别为每一行和列指定宽高&lt;/li&gt;
  &lt;li&gt;宽高的值可选 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 和数值
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; 表示 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 将按照元素的实际所需尺寸进行布局&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 表示行列在布局中的比例，&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 前面的数值表示比例值&lt;/li&gt;
      &lt;li&gt;数值使用的是 WPF/UWP 布局单位&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;元素在 Grid 中可跨行或跨列&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;基本上大家所熟知的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局差不多就这样么多了。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;如果想了解 WPF/UWP 的布局单位，可以阅读我之前的一篇文字&lt;a href=&quot;/post/introduce-uwp-effective-pixels-into-wpf&quot;&gt;将 UWP 的有效像素（Effective Pixels）引入 WPF - 吕毅&lt;/a&gt;&lt;/em&gt;。&lt;/p&gt;

&lt;p&gt;然而，事实上 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的布局行为才没有那么简单呢！它诡异的地方在于没有定义好多种复杂布局情况下的交叉行为。我写了中英两篇文章来说明了这些不太符合预期的行为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-bugs-of-grid&quot;&gt;WPF/UWP 的 Grid 布局竟然有 Bug，还不止一个！了解 Grid 中那些未定义的布局规则 - 吕毅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/the-bugs-of-grid-en&quot;&gt;The undefined behaviors of WPF Grid (the so-called bugs) - 吕毅&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;作为一个非常有潜力的 .NET Core 跨平台 UI 框架 Avalonia，应该认真定义好这些行为，而不是像 WPF/UWP 现有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 那样在某些情况下比较含糊，出现难以解释的布局行为。&lt;/p&gt;

&lt;h2 id=&quot;为这样的-grid-布局行为设计一套算法&quot;&gt;为这样的 Grid 布局行为设计一套算法&lt;/h2&gt;

&lt;p&gt;如果你熟知 WPF/UWP 的布局系统，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureOverride&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeOverride&lt;/code&gt; 一定不陌生，虽然它们只是布局的一部分（为什么是一部分？详见 &lt;a href=&quot;/post/features-and-limits-on-visual-uielement-frameworkelement&quot;&gt;Visual-&amp;gt;UIElement-&amp;gt;FrameworkElement，带来更多功能的同时也带来了更多的限制 - 吕毅&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;不过，写一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 确实只需要关心这两个函数就够了。&lt;code class=&quot;highlighter-rouge&quot;&gt;MeasureOverride&lt;/code&gt; 传入父级测量的可用尺寸，返回此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 测量发现所需的最小尺寸；&lt;code class=&quot;highlighter-rouge&quot;&gt;ArrangeOverride&lt;/code&gt; 传入父级实际可提供的可用尺寸，返回此 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 实际布局所用的尺寸。&lt;/p&gt;

&lt;h3 id=&quot;分析-grid-的布局思路&quot;&gt;分析 Grid 的布局思路&lt;/h3&gt;

&lt;p&gt;如果行或列设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的行或者列将为这个元素的尺寸进行适配，并且元素的所需尺寸也会影响到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的最小所需尺寸；如果行或列设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的行列不会为此元素适配，但是元素的所需尺寸依然会影响到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的最小所需尺寸。&lt;/p&gt;

&lt;p&gt;由于我们必须要计算 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的最小所需尺寸，所以整个布局过程中，必须得到每个行列的最小所需尺寸。这意味着，即便我们不能确定此行或此列的尺寸，或者甚至在父级尺寸确定的情况下能够确定此行或此列时，也应该计算最小尺寸。而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt;、元素的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DesiredSize&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 或者行列的最小值都会影响到此最小尺寸，所以这些都应该先考虑。而行或列的最大值应该在最后再考虑。&lt;/p&gt;

&lt;p&gt;于是，我们将整个布局过程分成以下几步：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;测量行列范围中包含 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 的元素（前者影响行列和最小尺寸，后者仅影响最小尺寸）&lt;/li&gt;
  &lt;li&gt;将所有的已确定尺寸确定&lt;/li&gt;
  &lt;li&gt;将所有的有最小尺寸，且 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 展开后超过此最小尺寸的行列按最小值确定&lt;/li&gt;
  &lt;li&gt;将所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Auto&lt;/code&gt; 行列确定&lt;/li&gt;
  &lt;li&gt;按照父级尺寸估算 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 的尺寸&lt;/li&gt;
  &lt;li&gt;计算 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 所需的最小尺寸&lt;/li&gt;
  &lt;li&gt;将估算缩得的尺寸作为实际尺寸进行测量&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;布局算法设计&quot;&gt;布局算法设计&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 的布局算法似乎难以用语言描述，不过，我可以尝试用更具体的文字用接近代码的方式来描述：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;测量过程
    &lt;ol&gt;
      &lt;li&gt;寻找所有行列范围中包含 Auto 和 * 的元素，使用全部可用尺寸提前测量&lt;/li&gt;
      &lt;li&gt;排除所有固定尺寸的行列，然后从总长中将其减掉&lt;/li&gt;
      &lt;li&gt;进行循环（以排除全部 min 要求的，&lt;em&gt;总长为负也要继续&lt;/em&gt;）
        &lt;ul&gt;
          &lt;li&gt;计算单位星长（单位星长 = 剩余总长 / 星数，最小为 0）&lt;/li&gt;
          &lt;li&gt;找出第一个不满足 min 要求的 *，置其长度为 min，排除此行列，然后从总长中将其减掉&lt;/li&gt;
          &lt;li&gt;所有的 * 检查完毕后，退出循环&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;进行循环（以排除全部 Auto，&lt;em&gt;总长为负也要继续&lt;/em&gt;）
        &lt;ul&gt;
          &lt;li&gt;计算单位星长（单位星长 = 剩余总长 / 星数，最小为 0）&lt;/li&gt;
          &lt;li&gt;找到第一个 Auto，找到对此 Auto 的约束（跨行列或不跨行列都需要）&lt;/li&gt;
          &lt;li&gt;对每个约束，检查目前尺寸是否满足约束（跨行列尺寸 &amp;gt;= Max(DesiredSize, min.Sum()）&lt;/li&gt;
          &lt;li&gt;满足约束的忽略，不满足的约束需要计算约束大出行列的尺寸值，将此值设定为此 Auto 的待选长度&lt;/li&gt;
          &lt;li&gt;当所有的约束检查完毕，在所有的待选长度中取最大值，设定为 Auto 的尺寸，排除此行列，然后从总长中将其减掉&lt;/li&gt;
          &lt;li&gt;所有的 Auto 检查完毕后，退出循环&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;按照父级尺寸估算 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 的尺寸
        &lt;ul&gt;
          &lt;li&gt;如果还有剩余长度，则将 * 展开，但需要考虑 &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; 行列的最小尺寸&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;确定 Grid 的 DesiredSize
        &lt;ul&gt;
          &lt;li&gt;如果剩余总长 &amp;gt;= 0，则 Grid.DesiredSize = 可用长度 - 剩余总长&lt;/li&gt;
          &lt;li&gt;如果剩余总长 &amp;lt; 0，则返回的 Grid.DesiredSize = 可用长度，实际需求的 Grid.DesiredSize = 可用长度 - 剩余总长&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果总长 &amp;gt;= 0，则进行循环（以确定剩余全部子元素的测量所用尺寸）；否则直接将剩余 * 全部设置为 0
        &lt;ul&gt;
          &lt;li&gt;进行循环
            &lt;ul&gt;
              &lt;li&gt;计算单位星长（单位星长 = 剩余总长 / 星数）&lt;/li&gt;
              &lt;li&gt;找出第一个不满足 max 要求的 *，置其长度为 max，排除此行列，然后从总长中将其减掉&lt;/li&gt;
              &lt;li&gt;所有的 * 检查完毕后，退出循环&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;至此，剩余的所有 * 都将不再有约束（即便元素 DesiredSize 不满足也无需处理，直接将元素裁剪）
            &lt;ul&gt;
              &lt;li&gt;计算单位星长（单位星长 = 剩余总长 / 星数）&lt;/li&gt;
              &lt;li&gt;计算所有剩余 * 的长度&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;至此，所有子元素测量所用尺寸已经全部确定，使用此尺寸对子元素进行测量&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;分开保存 1~5、6 计算后的所得结果，布局过程即结束&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;排列过程
    &lt;ol&gt;
      &lt;li&gt;如果排列可用尺寸大于测量可用尺寸，则测量过程中的全部计算部分需要重新进行（因为可能此前的 min 现在不满足条件）&lt;/li&gt;
      &lt;li&gt;如果排列可用尺寸等于测量可用尺寸，则直接使用测量第 6 步的结果，依次进行排列，不足部分的空间全部使用 0&lt;/li&gt;
      &lt;li&gt;如果排列可用尺寸小于测量可用尺寸，则重新执行测量第 6 步的计算，以确定新的行列尺寸，依次进行排列，不足部分的空间全部使用 0&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我在 Avalonia 的代码注释中，画出了每一个步骤的变化图。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 1. 测量行列范围中包含 `Auto` 或 `*` 的元素（前者影响行列和最小尺寸，后者仅影响最小尺寸）&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2. 将所有的已确定尺寸确定&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |  *  |  A  |  *  |  P  |  A  |  *  |  P  |     *     |  *  |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                   |#fix#|           |#fix#|&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 3. 将所有的有最小尺寸，且 `*` 展开后超过此最小尺寸的行列按最小值确定&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |  *  |  A  |  *  |  P  |  A  |  *  |  P  |     *     |  *  |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// | min | max |     |           | min |     |  min max  | max |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                   | fix |     |#fix#| fix |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 4. 将所有 `Auto` 行列确定&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |  *  |  A  |  *  |  P  |  A  |  *  |  P  |     *     |  *  |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// | min | max |     |           | min |     |  min max  | max |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//       |#fix#|     | fix |#fix#| fix | fix |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 5. 按照父级尺寸估算 `*` 的尺寸&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |  *  |  A  |  *  |  P  |  A  |  *  |  P  |     *     |  *  |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// | min | max |     |           | min |     |  min max  | max |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |#des#| fix |#des#| fix | fix | fix | fix |   #des#   |#des#|&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 6. 计算 `Grid` 所需的最小尺寸&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 7. 将估算缩得的尺寸作为实际尺寸进行测量&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |  *  |  A  |  *  |  P  |  A  |  *  |  P  |     *     |  *  |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// +-----------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// | min | max |     |           | min |     |  min max  | max |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// |#fix#| fix |#fix#| fix | fix | fix | fix |   #fix#   |#fix#|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;布局算法的代码&quot;&gt;布局算法的代码&lt;/h3&gt;

&lt;p&gt;为了让代码更容易调试，我专门写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;GridLayout&lt;/code&gt; 类来完成布局过程，而且 &lt;code class=&quot;highlighter-rouge&quot;&gt;GridLayout&lt;/code&gt; 的计算设计为与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 布局过程无关。做法是，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;GridLayout&lt;/code&gt; 的大部分方法设计为“纯方法”（纯方法只随便调用，调用此方法不会改变任何系统状态，只有拿到其返回值才会真正发挥作用）。&lt;/p&gt;

&lt;p&gt;具体的代码非常长，含单元测试供 1200+ 行，建议去 Avalonia 仓库查看：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Grid.cs&quot;&gt;Avalonia/Grid.cs at master · AvaloniaUI/Avalonia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Utils/GridLayout.cs&quot;&gt;Avalonia/GridLayout.cs at master · AvaloniaUI/Avalonia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;效果和性能&quot;&gt;效果和性能&lt;/h2&gt;

&lt;p&gt;在性能测试中，此算法还是表现不错的，以下是 Pull Request 中的性能测试截图（已经合并）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-05-20-15-50-53.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 20 May 2018 07:11:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/grid-layout-algorithm.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/grid-layout-algorithm.html</guid>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>algorithm</category>
        
      </item>
    
      <item>
        <title>安利一款非常好用的命令行参数库：McMaster.Extensions.CommandLineUtils</title>
        <description>&lt;p&gt;命令行参数解析想必是每一个命令行程序都难以避开的工程。这工程可小可大，但每次都写始终是在浪费时间。而且，不同人实现也千差万别，使得不同的命令行程序命令参数传入的体验总有差异。&lt;/p&gt;

&lt;p&gt;于是安利一款命令行工具库——&lt;code class=&quot;highlighter-rouge&quot;&gt;McMaster.Extensions.CommandLineUtils&lt;/code&gt;，它符合当下各大主流命令行工具的参数体验；而且，代码非常简洁。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;更新&lt;/strong&gt;：&lt;/p&gt;

&lt;p&gt;如果你之前阅读过我这篇博客，可能知道我之前推荐的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Extensions.CommandlineUtils&lt;/code&gt;，是微软出品；不过微软官方已经在 GitHub 上将此命令行项目重定向到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;aspnet/Common&lt;/code&gt;，原有的单独的命令行不复存在。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;McMaster.Extensions.CommandLineUtils&lt;/code&gt; 是微软官方指定的命令行仓库的正统 Folk 版本。&lt;/p&gt;

&lt;p&gt;它的仓库和 NuGet 包：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub: &lt;a href=&quot;https://github.com/natemcmaster/CommandLineUtils&quot;&gt;https://github.com/natemcmaster/CommandLineUtils&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;NuGet: &lt;a href=&quot;https://www.nuget.org/packages/McMaster.Extensions.CommandLineUtils&quot;&gt;https://www.nuget.org/packages/McMaster.Extensions.CommandLineUtils&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;体验超级简洁的代码吧&quot;&gt;体验超级简洁的代码吧！&lt;/h2&gt;

&lt;p&gt;我正在自己的项目中采用这款库，项目名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;mdmeta&lt;/code&gt;，用于自动生成 Markdown 前的元数据标签，写博客非常方便。&lt;/p&gt;

&lt;p&gt;项目地址：&lt;a href=&quot;https://github.com/walterlv/markdown-metadata&quot;&gt;walterlv/markdown-metadata: Markdown Metadata (also called mdmeta) is a tool to generate and manage the front matter metadata. It is a cross-platform console app based on .Net Core 2.0.&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;体验主流的命令行参数体验&quot;&gt;体验主流的命令行参数体验&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 不带任何参数&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 一个简单的命令&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 一个带参数（Argument）的简单的命令&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 一个带选项（Option）的简单命令&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--upper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 一个带参数（Argument）带选项（Option）且选项中带值的简单命令&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 一个带参数（Argument）带多种选项（Option）且部分选项中带多个值的简单命令&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mdmeta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--repeat-count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;| &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;体验库的-builder-api&quot;&gt;体验库的 Builder API&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;McMaster.Extensions.CommandLineUtils&lt;/code&gt; 使用 Builder API 配出以上的命令，代码非常简洁。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandLineApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mdmeta&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HelpOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-?|-h|--help&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowHelp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;echo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;输出用户输入的文字。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HelpOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-?|-h|-help&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordsArgument&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[words]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;指定需要输出的文字。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repeatOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-r|--repeat-count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;指定输出重复次数&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandOptionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SingleValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;upperOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--upper&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;指定是否全部大写&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandOptionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;separatorOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-s|--separator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;指定重复输出用户文字时重复之间应该使用的分隔符，可以指定多个，这将依次应用到每一次分割。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandOptionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultipleValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 在这里使用上面各种 Argument 和 Option 的 Value 或 Values 属性拿值。&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;体验我封装的命令行参数配置&quot;&gt;体验我封装的命令行参数配置&lt;/h3&gt;

&lt;p&gt;原生库配置命令行参数已经非常方便了，几乎是一行一个功能，但 &lt;code class=&quot;highlighter-rouge&quot;&gt;lambda&lt;/code&gt; 表达式嵌套太多是一个问题，会导致代码随着参数种类的增多变得急剧膨胀；于是我针对原生库做了一个基于反射的版本。于是，实现一个命令行参数只需要写这些代码就够啦：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;更新&lt;/strong&gt;：&lt;code class=&quot;highlighter-rouge&quot;&gt;McMaster.Extensions.CommandLineUtils&lt;/code&gt; 接手微软之后，也添加了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Attribute&lt;/code&gt; 的 API，使用方法与下面的大同小异。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CommandMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;echo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Output users command at specified format.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SampleTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandTask&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repeatCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CommandArgument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[words]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The words the user wants to output.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Words&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CommandOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-r|--repeat-count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Indicates how many times to output the users words.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RepeatCountRaw&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repeatCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repeatCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CommandOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--upper&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Indicates that whether all words should be in upper case.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpperCase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CommandOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-s|--separator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Specify a string to split each repeat.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Separators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 当用户敲入的命令已准备好，上面的参数准备好，那么这个函数就会在这里执行啦。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你一定会吐槽代码变多了。确实如此！但是，当命令的种类和参数的种类变得急剧膨胀的时候，这种方式可以将各种命令都隔离开来。于是，你只需要专注于实现自己的命令就好啦！&lt;/p&gt;

&lt;p&gt;将以下这些文件放入自己的项目中即可立刻写出上面的代码（注意 &lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数也是需要的，因为它启动了反射）：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;如果发现这一行的后面不是代码，那么极有可能是被不小心屏蔽了，请手动访问：&lt;a href=&quot;https://gitee.com/walterlv/codes/0wjc7mlvgipr4uzn8a3qo76&quot;&gt;gitee.com/codes&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/0a2257c30e8c175cae657b0058f5421c.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;支持的平台&quot;&gt;支持的平台&lt;/h2&gt;

&lt;p&gt;支持 .Net Standard 1.3，这意味着 .Net Core 可以使用，.NET Framework 4.5.1 及以上即可使用。这意味着可以很随意地跨全平台。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/iamarcel/8047384bfbe9941e52817cf14a79dc34&quot;&gt;Creating Neat .NET Core Command Line Apps&lt;/a&gt;
-&lt;a href=&quot;https://github.com/natemcmaster/CommandLineUtils/&quot;&gt;natemcmaster/CommandLineUtils: Command line parsing and utilities for .NET Core and .NET Framework.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 26 Apr 2018 12:39:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/mcmaster-extensions-commandlineutils.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/mcmaster-extensions-commandlineutils.html</guid>
        
        
        <category>dotnet</category>
        
        <category>dotnet-core</category>
        
        <category>dotnet-standard</category>
        
      </item>
    
      <item>
        <title>冷算法：自动生成代码标识符（类名、方法名、变量名）</title>
        <description>&lt;p&gt;竟然有小伙伴喜欢在编写代码时使用随机字符当作类名、方法名、变量名，例如这一篇博客里的代码：&lt;a href=&quot;https://blog.lindexi.com/post/%E4%BD%BF%E7%94%A8-Resharper-%E7%89%B9%E6%80%A7.html&quot;&gt;使用 Resharper 特性 - 林德熙&lt;/a&gt;。既然随机，那也随机得像一些啊！于是我改进了标识符的随机算法，使得生成的标识符更像真实单词的组合。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;看看标识符的生成效果吧！0、2、4……行是 PascalCase，即首字母大写的；1、3、5……行是 camelCase 即首字母小写的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-26-07-42-48.png&quot; alt=&quot;自动生成的标识符&quot; /&gt;&lt;br /&gt;
▲ 是不是感觉甚至能读出来？&lt;/p&gt;

&lt;p&gt;嗯嗯，因为生成规则中考虑到了辅音和元音的组合，而且……嗯……还考虑到了部件出现的概率。&lt;/p&gt;

&lt;p&gt;比如一个单词中的音节数，单音节概率 44%，双音节概率 31%，三音节概率 19%，四音节概率 6%。而这样的概率是通过一个幂函数来实现的。具体来说，是下面这个函数：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-26-08-02-01.png&quot; alt=&quot;音节数&quot; /&gt;&lt;/p&gt;

&lt;p&gt;好吧，把我的源码放出来：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RandomIdentifier&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Random&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syllableCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vowel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToTitleCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consonant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vowel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Consonants&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;q&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;k&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;tr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;st&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vowels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;u&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;u&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;ar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;as&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ai&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;air&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;al&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;aw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;ee&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ea&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ear&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;em&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;er&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;el&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ere&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;is&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ir&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;ou&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;or&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;oo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ou&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;ur&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而使用方法，则是简单的一个调用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RandomIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pascal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;camel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; 生成首字母大写的版本，传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; 生成首字母小写的版本。&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Apr 2018 00:04:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/algorithm-of-generating-random-identifiers.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/algorithm-of-generating-random-identifiers.html</guid>
        
        
        <category>algorithm</category>
        
      </item>
    
      <item>
        <title>利用 TypeConverter，转换字符串和各种类型只需写一个函数</title>
        <description>&lt;p&gt;&lt;em&gt;本文代码基于 .NET Framework 实现。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;本来只想进行简单的配置存储的，不料发现 .NET 的基本类型多达十多种。于是，如果写成下面这样，那代码可就太多了哦：&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 注：`Configurator`是我的配置类，用于读写字符串的。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetBoolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 还没完，这才 1.5 种而已。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ……&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;尝试使用泛型&quot;&gt;尝试使用泛型&lt;/h2&gt;

&lt;p&gt;这些方法都比较相似，于是自然而然想到了&lt;strong&gt;泛型&lt;/strong&gt;，所以写出了这段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// T value = 某种通用的转换(@string); // 问题来了，这里该怎么写？&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里就郁闷了，因为虽然方法内部的实现都长得差不多，但他们之间除了名字相同之外（比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Parse&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;ToString&lt;/code&gt;），并没有什么联系；这样，便不能使用统一的接口或者抽象类等等来调用。&lt;/p&gt;

&lt;h2 id=&quot;尝试使用反射&quot;&gt;尝试使用反射&lt;/h2&gt;

&lt;p&gt;既然名字类似，那自然又能想到反射。可是，拥有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Parse&lt;/code&gt; 的类型并不多，&lt;code class=&quot;highlighter-rouge&quot;&gt;ToString&lt;/code&gt; 中能传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;CultureInfo.InvariantCulture&lt;/code&gt; 且参数顺序保持一致的类型也少的可怜。于是，反射只能保证写得出来代码，却并不能保证多种类型的支持。&lt;/p&gt;

&lt;p&gt;另外想到一点，&lt;code class=&quot;highlighter-rouge&quot;&gt;Int32&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TryParse&lt;/code&gt; 中有 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 关键字修饰的参数，反射能否支持呢？&lt;a href=&quot;https://stackoverflow.com/questions/2438065/c-sharp-reflection-how-can-i-invoke-a-method-with-an-out-parameter&quot;&gt;StackOverflow 上找到了答案&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You invoke a method with an out parameter via reflection just like any other method. The difference is that the returned value will be copied back into the parameter array so you can access it from the calling function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_DownloadDataInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;意思是说，这在反射中不用作什么考虑，传入的参数数组天然就已经支持了 &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; 关键字。&lt;/p&gt;

&lt;h2 id=&quot;尝试寻找更通用的方案&quot;&gt;尝试寻找更通用的方案&lt;/h2&gt;

&lt;p&gt;在 Google 上继续漫游，在 StackOverflow 上发现这篇讨论：&lt;a href=&quot;https://stackoverflow.com/questions/2922855/how-to-convert-string-to-any-type&quot;&gt;How to convert string to any type&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;最高票答案给出的回复是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.ComponentModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;TypeConverter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;这可打开了思路，原来 .NET Framework 内部已经有了这种转换机制和相关的方法。于是用这种方法修改我的方法，成了这样子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertFromInvariantString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;@string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configurator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertToInvariantString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编写单元测试发现，这种方法能够支持的类型真的非常多，&lt;code class=&quot;highlighter-rouge&quot;&gt;byte&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;short&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;ushort&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;uint&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;long&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;ulong&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;bool&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;decimal&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Point&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Vector&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Size&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Rect&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Color&lt;/code&gt;……&lt;/p&gt;

&lt;p&gt;看看代码中 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeDescriptor.GetConverter&lt;/code&gt; 的返回值发现是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt; 类型的，而我们在 WPF 的 xaml 中编写自定义类型时，经常需要执行此类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt;。凭这一点可以大胆猜测，xaml 中能够通过字符串编写的类型均可以通过这种方式进行转换。然而，目前我还为对此进行验证。&lt;/p&gt;

&lt;h2 id=&quot;验证猜想&quot;&gt;验证猜想&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;去 &lt;a href=&quot;https://referencesource.microsoft.com/#System/compmod/system/componentmodel/TypeDescriptor.cs,99bfa42a7de642f9&quot;&gt;https://referencesource.microsoft.com/&lt;/a&gt; 看 &lt;a href=&quot;https://referencesource.microsoft.com/#System/compmod/system/componentmodel/TypeDescriptor.cs,99bfa42a7de642f9&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TypeDescriptor.GetConverter&lt;/code&gt;&lt;/a&gt; 的源码（&lt;a href=&quot;https://referencesource.microsoft.com/#System/compmod/system/componentmodel/TypeDescriptor.cs,99bfa42a7de642f9&quot;&gt;点击进入&lt;/a&gt;）。&lt;/li&gt;
  &lt;li&gt;尝试自定义一个类型，在类型上标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt;，并对此类进行测试。&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Sun, 22 Apr 2018 23:31:32 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2017/01/17/convert-using-type-converter.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2017/01/17/convert-using-type-converter.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>命令“&quot;xxx.exe&quot; xxx”已退出，代码为 3/123/9009。VS 的这些编译错误代码代表了什么意思？</title>
        <description>&lt;p&gt;我们在 &lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 中输入常见命令的时候，如果命令输入错误或使用错误，&lt;code class=&quot;highlighter-rouge&quot;&gt;cmd&lt;/code&gt; 中会提示错误原因，帮助我们定位并解决问题。然而如果相同的命令放到了 Visual Studio 的生成事件中，我们就只能得到 Visual Studio 返回的错误代码了。为了能够快速地根据错误代码大致猜测错误原因，本文整理了一部分错误代码的通用原因。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;命令“&lt;span class=&quot;s2&quot;&gt;&quot;xxx.exe&quot;&lt;/span&gt; xxx”已退出，代码为 n。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;命令的格式为：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;命令 参数1 参数2 参数3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div id=&quot;top&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;错误代码-3&quot;&gt;错误代码: 3&lt;/h2&gt;

&lt;p&gt;系统找不到指定的路径。&lt;/p&gt;

&lt;p&gt;这意味着我们在编译生成命令中写的“命令”部分，在那个路径下并不存在命令中写的可执行文件。比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;文件或文件夹的名称写错了，或者写上级目录时“..”的层数不对&lt;/li&gt;
  &lt;li&gt;依赖的是一部分人开发环境中才有的可执行文件，如果你的开发环境中没有这个文件，就会是此错误&lt;/li&gt;
  &lt;li&gt;依赖的文件需要提前生成但还没有生成，某些 BT 的项目会要求先执行一些编译命令以生成命令的可执行文件&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;错误代码-123&quot;&gt;错误代码: 123&lt;/h2&gt;

&lt;p&gt;文件名、目录名或卷标语法不正确。&lt;/p&gt;

&lt;p&gt;如果路径字符串根本无法拼出路径，就会引发此错误。比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;D:\walterlv.github.io\D:\Bin\Debug&lt;/code&gt; 这样的路径就是不合理的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一般人当然不会写出这样的命令出来，但如果路径中间有一些环境变量作为占位符，实际执行时本来期望填入相对路径的地方填入了绝对路径就会这样。&lt;/p&gt;

&lt;h2 id=&quot;错误代码-9009&quot;&gt;错误代码: 9009&lt;/h2&gt;

&lt;p&gt;‘xxx’ 不是内部或外部命令，也不是可运行的程序或批处理文件。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;命令写错了，而不是可执行文件写错了
    &lt;ul&gt;
      &lt;li&gt;比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;xcopy&lt;/code&gt; 写成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;xcapy&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;可执行文件因为缺少引号包括路径中的空格，导致被识别成无法识别的命令
    &lt;ul&gt;
      &lt;li&gt;比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program Files\XXX&lt;/code&gt; 因为没有引号的包裹，被识别成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Program&lt;/code&gt; 命令&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;持续整理中……&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Apr 2018 10:56:22 +0000</pubDate>
        <link>https://blog.walterlv.com/post/error-code-in-vs-build-events.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/error-code-in-vs-build-events.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>VS 编译太慢了吗？新建解决方案配置关闭一部分项目的编译</title>
        <description>&lt;p&gt;手头的解决方案真大！里面的项目个数达到了 30 个或是 50 个？然而接近一半是单元测试项目和辅助工具。再加上一些不尽如人意的项目优化，编译速度真的是无力吐槽。幸好 Visual Studio 提供了解决方案配置功能，可以让我们在编译时略过一些项目。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在解决方案上右击选择“属性”，我们将打开“解决方案属性页”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-08-06.png&quot; alt=&quot;解决方案 - 属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在属性页种选择“配置”，则可以开始指定项目是否生成或部署。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-09-42.png&quot; alt=&quot;解决方案属性页 - 配置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，我们只在这两种情况下才不需要单元测试：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;有些电脑性能太差，带不动这么大的解决方案&lt;/li&gt;
  &lt;li&gt;Release 下是用于发布产品的，不需要编译和执行单元测试（&lt;em&gt;这并不是说发布产品前不用跑单元测试&lt;/em&gt;）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于第一种情况，我们需要新建一个解决方案配置来应对，让电脑性能较差的开发者使用单独的配置；第二种情况则可以直接在 Release 上改。&lt;/p&gt;

&lt;p&gt;要新建配置，需要进入“配置管理器”，在“活动解决方案配置”中选择“新建”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-16-46.png&quot; alt=&quot;新建配置&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后补充新建的信息：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-19-27.png&quot; alt=&quot;填写新建配置的信息&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在新的解决方案配置和 Release 中取消单元测试项目的生成。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-20-14.png&quot; alt=&quot;取消生成 - Debug-WithoutTests&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-21-07.png&quot; alt=&quot;取消生成 - Release&quot; /&gt;&lt;/p&gt;

&lt;p&gt;关闭和确认对话框之后，就会发现 Visual Studio 中之前用于选择 Debug/Release 的下拉框现在多出了 Debug-WithoutTests 配置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-04-03-08-21-51.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;并且如果选中了 Debug-WithoutTests 或者 Release 后，无论是生成还是重新生成解决方案，更或者是使用命令行编译，都不会生成单元测试项目。&lt;/p&gt;

&lt;p&gt;额外的，这些配置是团队共享的，因为它储存在 sln 文件中；至于哪一个处于选中状态是团队成员自己的配置，不会互相影响。&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Apr 2018 23:59:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/skip-building-using-solution-configuration.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/skip-building-using-solution-configuration.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>利用 ReSharper 自定义代码中的错误模式，在代码审查之前就发现并修改错误</title>
        <description>&lt;p&gt;多人协作开发的项目总会遇到代码编写风格上的差异。一般工具都能帮我们将常见的差异统一起来——例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; 的换行；但也有一些不那么通用，但项目中却经常会出现的写法也需要统一。&lt;/p&gt;

&lt;p&gt;例如将单元测试中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assert.AreEqual(foo.GetType(), typeof(Foo));&lt;/code&gt; 换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assert.IsInstanceOfType(foo, typeof(Foo));&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;阅读本文将学习如何使用 ReSharper 的 Custom Pattern 功能来完成这样的警告和转换。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;预览效果&quot;&gt;预览效果&lt;/h2&gt;

&lt;p&gt;我们团队中自定义了一个代码风格规范，在单元测试中 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assert.AreEqual(foo.GetType(), typeof(Foo));&lt;/code&gt; 应该被换成 &lt;code class=&quot;highlighter-rouge&quot;&gt;Assert.IsInstanceOfType(foo, typeof(Foo));&lt;/code&gt;。于是，ReSharper 会给出警告，并给出推荐的写法；如果遵循 ReSharper 的建议，ReSharper 将自动为我们修改代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-18-51-20.png&quot; alt=&quot;给出警告，并提供建议&quot; /&gt;&lt;br /&gt;
▲ 给出警告，并提供建议&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-38-40.png&quot; alt=&quot;可以遵循建议&quot; /&gt;&lt;br /&gt;
▲ 可以遵循建议&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-18-53-27.png&quot; alt=&quot;然后代码就被修改成我们建议的写法了&quot; /&gt;&lt;br /&gt;
▲ 然后代码就被修改成我们建议的写法了&lt;/p&gt;

&lt;h2 id=&quot;开始编写自定义模式&quot;&gt;开始编写自定义模式&lt;/h2&gt;

&lt;p&gt;我们需要打开 ReSharper 的选项窗口，然后在里面找到“自定义模式”：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-00-36.png&quot; alt=&quot;Options&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-01-32.png&quot; alt=&quot;Custom Patterns&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“Add Pattern”之后，我们就可以开始编写 Custom Pattern 了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-08-23.png&quot; alt=&quot;Add Highlighting Pattern&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了快速开始，可以将下面的两行代码分别复制到两个黑框中。（如果你只看到了一个黑框，请在右上角将“Find”按钮切换到“Replace”按钮。）&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 将下面这一句话复制到第一个黑色框中。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 将下面这一句话复制到第二个黑色框中。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsInstanceOfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时，占位符框中就会出现我们编写的两个占位符：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-12-50.png&quot; alt=&quot;占位符列表&quot; /&gt;&lt;br /&gt;
▲ 占位符列表&lt;/p&gt;

&lt;p&gt;我们需要将 &lt;code class=&quot;highlighter-rouge&quot;&gt;instance&lt;/code&gt; 占位符从表达式修改为标识符：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-13-30.png&quot; alt=&quot;标识符&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;解释一下这几项的意思：&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Argument Placeholder&lt;/strong&gt; 参数占位符
      &lt;ul&gt;
        &lt;li&gt;意味着这里是参数列表，可以是一个或多个参数，中间用逗号分隔。参数数量可以额外指定。&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Expression Placeholder&lt;/strong&gt; 表达式占位符
      &lt;ul&gt;
        &lt;li&gt;形如 &lt;code class=&quot;highlighter-rouge&quot;&gt;foo.Bar()&lt;/code&gt;，注意，分号并不是表达式的一部分。&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Identifier Placeholder&lt;/strong&gt; 标识符占位符&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Statement Placeholder&lt;/strong&gt; 语句占位符
      &lt;ul&gt;
        &lt;li&gt;形如 &lt;code class=&quot;highlighter-rouge&quot;&gt;if (foo is null) throw new ArgumentNullException(nameof(foo));&lt;/code&gt;，注意，分号属于语句的一部分。&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Type Placeholder&lt;/strong&gt; 类型占位符
      &lt;ul&gt;
        &lt;li&gt;形如 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo&lt;/code&gt;，或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Walterlv.Demo.Foo&lt;/code&gt;。&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;确定之后我们填写其他的信息：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Pattern severity：警告
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;如果你需要，修改成“错误”也是可以的；事实上我们的项目中就是标记为错误，这样找出的代码就会是红色的错误下划线了。&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Suppression key：&lt;code class=&quot;highlighter-rouge&quot;&gt;AssertEqualToInstanceOfType&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;（可选）只有指定了用于阻止检查的标识字符串，才可以在特殊情况下用以下几种方法阻止检查；否则你将对错误无能为力。
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;// ReSharper disable once AssertEqualToInstanceOfType&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[SuppressMessage(&quot;ReSharper&quot;, &quot;AssertEqualToInstanceOfType&quot;)]&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;上面的 Description：&lt;code class=&quot;highlighter-rouge&quot;&gt;建议简化成 InstanceOfType 以提升可读性。&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这将在鼠标滑到找到的语句上面时给出提示。&lt;br /&gt;
  &lt;img src=&quot;/static/posts/2018-03-20-18-51-20.png&quot; alt=&quot;提示&quot; /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;下面的 Description：&lt;code class=&quot;highlighter-rouge&quot;&gt;简化成 InstanceOfType&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;这将在在 Alt+Enter 时出现的重构列表中显示&lt;br /&gt;
  &lt;img src=&quot;/static/posts/2018-03-20-19-38-40.png&quot; alt=&quot;可以遵循建议&quot; /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;设置完之后，“Edit Highlighting Pattern”窗口应该是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-29-35.png&quot; alt=&quot;设置完的 Edit Highlighting Pattern 窗口&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然，在“Custom Pattern”列表中也可以统一设置所有模式的警告级别。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-26-14.png&quot; alt=&quot;Warning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后，&lt;strong&gt;把这些规则保存到团队共享中，那么所有安装了 ReSharper 的此项目的团队成员都将遵循这一套规则&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-20-05-57.png&quot; alt=&quot;保存到团队&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;自己动手发掘潜能&quot;&gt;自己动手，发掘潜能&lt;/h2&gt;

&lt;p&gt;Custom Pattern 功能只是为了给我们一个格式转换吗？才不止是这样哦！它能够帮助我们发现一些潜在的错误。&lt;/p&gt;

&lt;p&gt;例如使用 &lt;a href=&quot;https://github.com/dotnet-campus/MSTestEnhancer/&quot;&gt;MSTestEnhancer&lt;/a&gt; 进行单元测试时，如果使用了它推荐的单元测试风格，就应该配套使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContractTestCase&lt;/code&gt; 特性，如果不这么写，必定意味着错误。&lt;/p&gt;

&lt;p&gt;于是，我们可以编写一个自定义模式来发现和修改这样的错误。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-51-18.png&quot; alt=&quot;更复杂的例子&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你认为可以怎么写呢？我在下面给出了我的写法。你还可以发掘出更多的潜能吗？非常期待！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-20-19-53-43.png&quot; alt=&quot;配置 MSTestEnhancer 的检查&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 20 Mar 2018 12:35:30 +0000</pubDate>
        <link>https://blog.walterlv.com/post/analyze-and-fix-code-using-resharper-custom-pattern.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/analyze-and-fix-code-using-resharper-custom-pattern.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>csharp</category>
        
        <category>resharper</category>
        
      </item>
    
      <item>
        <title>WPF 程序无法触摸操作？我们一起来找原因和解决方法！</title>
        <description>&lt;p&gt;WPF 自诞生以来就带着微软先生的傲慢。微软说 WPF 支持触摸，于是 WPF 就真的支持触摸了。对，我说的是“支持触摸”，那种摸上去能点能动的；偶尔还能带点儿多指的炫酷效果。但是，WPF 推出那会儿，绝大部分开发者都还没有触摸屏呢，开发个程序要怎么验证支不支持触摸呢？微软先生无奈地决定——你写鼠标的代码就好了，我帮你转换！于是……一大波 BUG 袭来……&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;wpf-触摸失效的分类&quot;&gt;WPF 触摸失效的分类&lt;/h2&gt;

&lt;p&gt;我将 WPF 的触摸失效总结成三种不同的类型。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;触摸下 Stylus/Touch 事件正常触发，但不提升为 Mouse 事件；导致仅使用 Mouse 事件的控件无法使用&lt;/li&gt;
  &lt;li&gt;触摸下 Stylus/Touch 有触发，但触发点位置在 (0, 0) 处或上一个触摸点处；导致即使触发了，当前控件也收不到&lt;/li&gt;
  &lt;li&gt;触摸下无 Stylus/Touch 事件，也不提升为 Mouse 事件，但鼠标下有 Mouse 事件；导致整个界面完全无法触摸使用&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;第一种情况&quot;&gt;第一种情况&lt;/h3&gt;

&lt;p&gt;使用触摸或者触笔操作时，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 事件中发生了任何异常，会导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusLogic.PostProcessInput&lt;/code&gt; 的后续逻辑不会正确执行，这就包括了用于清理触控资源的 StylusTouchDevice.OnDeactivate 方法。需要注意的是：&lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 事件不止是 &lt;code class=&quot;highlighter-rouge&quot;&gt;TouchUp&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusUp&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;MouseUp&lt;/code&gt; 也会引发这样的触摸失效。&lt;/p&gt;

&lt;p&gt;而在 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusTouchDevice.OnDeactivate&lt;/code&gt; 方法中，会重置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusLogic.CurrentMousePromotionStylusDevice&lt;/code&gt; 属性为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;NoMousePromotionStylusDevice&lt;/code&gt;。此方法不执行会直接导致 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusLogic.ShouldPromoteToMouse&lt;/code&gt; 方法对当前触控设备的判断出现错误，持续返回 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，即不会再执行触控转鼠标的逻辑，出现触摸无效的现象。&lt;/p&gt;

&lt;h3 id=&quot;第二种情况&quot;&gt;第二种情况&lt;/h3&gt;

&lt;p&gt;如果 WPF 的 StylusUp 事件被阻断（例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt;，或者在 StylusUp 事件中弹出一个模态窗口），则下一次触摸时获取到的点坐标将是上一次被阻断时的点坐标。于是，阻断后的第一次点击必将点中之前点的那个点，而不管现在点中了什么。如果阻断时点在新窗口外，则几乎相当于触摸失效。需要注意的是，这种情况下 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseUp&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt; 是可以使用而不会导致触摸失效的。&lt;/p&gt;

&lt;h3 id=&quot;第三种情况&quot;&gt;第三种情况&lt;/h3&gt;

&lt;p&gt;WPF 程序在启动期间，如果触摸组件发生了异常，极有可能会使得触摸根本就没有初始化成功！&lt;/p&gt;

&lt;p&gt;比如，&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Input.StylusLogic.RegisterStylusDeviceCore(StylusDevice stylusDevice)&lt;/code&gt; 方法在启动时抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.InvalidOperationException&lt;/code&gt;，虽然内部有 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;，但实际获取到的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TabletDevice&lt;/code&gt; 个数是 0 个，根本无法获取触摸设备，于是触摸无效。&lt;/p&gt;

&lt;p&gt;或者，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;WorkerOperationGetTabletsInfo.OnDoWork&lt;/code&gt; 方法中，获取到了错误的触摸设备个数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IPimcManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pimcManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PimcManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pimcManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTabletCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;解决之道&quot;&gt;解决之道&lt;/h2&gt;

&lt;p&gt;目前为止，这三种问题都没有根本的解决办法，但是我们可以规避。&lt;/p&gt;

&lt;h3 id=&quot;第一种情况-1&quot;&gt;第一种情况&lt;/h3&gt;

&lt;p&gt;我们没有办法阻止每一处的 Up 事件，所以我的做法是在禁止那些可能会在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 中引发异常的操作监听 &lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 事件，而是统一由我封装好的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Down/Move/Up&lt;/code&gt; 中进行分发。在我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 所有异常，随后延迟引发。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 分发真正业务上的 Up 事件。&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DeliverUpEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 使用触摸或者触笔操作时，如果 Up 事件中发生了任何异常，会导致 StylusLogic.PostProcessInput 的后续逻辑不会正确执行，&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这就包括了用于清理触控资源的 StylusTouchDevice.OnDeactivate 方法。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 而在 StylusTouchDevice.OnDeactivate 方法中，会重置 StylusLogic.CurrentMousePromotionStylusDevice 属性&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 为 null 或 NoMousePromotionStylusDevice。此方法不执行会直接导致 StylusLogic.ShouldPromoteToMouse 方法&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 对当前触控设备的判断出现错误，持续返回 false，即不会再执行触控转鼠标的逻辑，出现触摸无效的现象。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这里通过 InvokeAsync 的方式再次抛出异常是为了在保证 Stylus 逻辑不出错的情况下，将异常暴露。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;第二种情况-1&quot;&gt;第二种情况&lt;/h3&gt;

&lt;p&gt;一样的，我们没有办法阻止每一处的 Up 事件。于是我们只能要求多人开发项目中的每一位开发人员都注意不要在 &lt;code class=&quot;highlighter-rouge&quot;&gt;StylusUp&lt;/code&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;e.Handled = true&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然而，要求每一个人都这么做是不现实的，尤其是团队成员不稳定的情况下。目前我还没有找到具体可实施的自动化的解决办法，不过我最近正在尝试的 Roslyn 扩展可能可以解决这样的问题。有关 Roslyn 扩展的开发，可以阅读我的另一篇文章：&lt;a href=&quot;/post/analysis-code-of-existed-projects-using-roslyn&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;第三种情况-1&quot;&gt;第三种情况&lt;/h3&gt;

&lt;p&gt;启动时触摸设备获取错误的问题我还没有一个彻底的解决方案，目前是检测第一次机会异常，并在发现错误堆栈是以上情况的时候重新启动应用程序。能够采取这样的策略是因为此异常发生在我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;App&lt;/code&gt; 类初始化之后 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 显示出来之前。&lt;/p&gt;

&lt;h3 id=&quot;更多的想法&quot;&gt;更多的想法&lt;/h3&gt;

&lt;p&gt;期待你有更多的想法，我希望在我们的交流之下，能够帮助更多人发现和解决 WPF 的触摸失效问题，甚至更多 WPF 的疑难杂症。&lt;/p&gt;
</description>
        <pubDate>Mon, 19 Mar 2018 11:30:45 +0000</pubDate>
        <link>https://blog.walterlv.com/wpf/2017/09/12/touch-not-work-in-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/wpf/2017/09/12/touch-not-work-in-wpf.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>在移动端打开 Google 的网页快照</title>
        <description>&lt;p&gt;Google 的网页快照功能在原网页意外挂掉的时候能够临时为我们提供网页内的信息。例如我们要搜索的某项技术资料来源于某个个人站点，而现在他的域名到期了没有续费；例如我现在的博客在部署期间挂掉了，不能继续访问。这时 Google 网页快照都能够帮我们临时访问网页缓存。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;PC 端的网页快照很容易找到并且点开：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-07-49-20.png&quot; alt=&quot;网页快照&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而移动端就不那么幸运了，找不到那个打开快照的小按钮：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-03-09-07-52-46.png&quot; alt=&quot;移动端没有网页快照&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个时候，可以复制以下网址到地址栏中，将预留的 &lt;code class=&quot;highlighter-rouge&quot;&gt;网址&lt;/code&gt; 二字替换成希望点进去但挂掉了的链接地址（可以从 Google 的搜索结果页点开去地址栏复制）。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://webcache.googleusercontent.com/search?cache:网址
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;多数时候我们能在缓存中访问到完整的网页，如果目标站点的域名挂掉，那么可能我们只能访问到支离破碎的纯 html 了。&lt;/p&gt;
</description>
        <pubDate>Thu, 08 Mar 2018 23:55:03 +0000</pubDate>
        <link>https://blog.walterlv.com/post/view-google-cache-in-mobile.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/view-google-cache-in-mobile.html</guid>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>在 HTML 超链接上添加可交互的 ToolTip</title>
        <description>&lt;p&gt;当鼠标滑过超链接的那一刻，我们都能想象出一个熟悉的白色提示框从鼠标指针所在的位置淡入。那是 ToolTip 提示框。HTML 中我们能通过简单的属性设置获得 ToolTip，但如果希望 ToolTip 是能交互的，那么就阅读本文吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;原生-tooltip&quot;&gt;原生 ToolTip&lt;/h2&gt;

&lt;p&gt;先来看看 HTML 原生自带的 ToolTip：&lt;/p&gt;

&lt;p&gt;&lt;a title=&quot;你看到了什么？对，这就是原生自带的 ToolTip！&quot; href=&quot;#&quot;&gt;请将鼠标划至这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;代码非常简单：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你看到了什么？对，这就是原生自带的 ToolTip！&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;请将鼠标划至这里&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;可交互-tooltip&quot;&gt;可交互 ToolTip&lt;/h2&gt;

&lt;p&gt;可交互的 ToolTip 就没那么幸运了，没有自带。于是，我们可考虑通过自己拼接的 html 容器来实现。效果像这样：&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;tooltip&quot;&gt;
    &lt;span&gt;&lt;a href=&quot;https://walterlv.github.io&quot;&gt;请将鼠标划至这里&lt;/a&gt;&lt;/span&gt;
    &lt;span class=&quot;tooltip-text&quot; style=&quot;background:#eee;padding:2px 0;border-radius: 0.5rem;&quot;&gt;这是 &lt;a href=&quot;https://walterlv.github.io&quot;&gt;内部的链接哦&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;这是靠一组 html 容器和一些配套的 css 来实现的。&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tooltip&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;请将鼠标划至这里&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tooltip-text&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;这是&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;内部的链接哦&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.tooltip&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.tooltip-text&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-7rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#eee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.tooltip&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.tooltip-text&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;visible&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本博客底部的版权信息中使用到了这种交互式 ToolTip。&lt;/p&gt;
</description>
        <pubDate>Thu, 08 Mar 2018 10:39:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-tooltip-for-html-a.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-tooltip-for-html-a.html</guid>
        
        
        <category>web</category>
        
        <category>html</category>
        
        <category>css</category>
        
      </item>
    
      <item>
        <title>如何删除 Windows 10 系统生成的 WindowsApps 文件夹</title>
        <description>&lt;p&gt;如果曾经修改过 Windows 10 应用安装路径到非系统盘，那么那个盘下就会生成一些文件夹。如果以后重装了系统或者应用删除了，挪位置了，那些文件夹依然在那里——删不掉！&lt;/p&gt;

&lt;p&gt;大家都知道这是权限问题，然而如何修改权限以便成功删除呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-27-02.png&quot; alt=&quot;更改应用的保存位置&quot; /&gt;&lt;br /&gt;
▲ 更改应用的保存位置&lt;/p&gt;

&lt;p&gt;那么，现在开始解决删不掉的问题吧！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-35-59.png&quot; alt=&quot;删不掉&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-42-28.png&quot; alt=&quot;进不去&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;第一步属性安全高级&quot;&gt;第一步：属性→安全→高级&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-43-28.png&quot; alt=&quot;属性→安全→高级&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-44-09.png&quot; alt=&quot;高级安全设置&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步更改所有者&quot;&gt;第二步：更改所有者&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-44-57.png&quot; alt=&quot;更改所有者&quot; /&gt;&lt;br /&gt;
▲ 更改所有者&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-45-24.png&quot; alt=&quot;输入自己的用户名&quot; /&gt;&lt;br /&gt;
▲ 在这里输入自己的用户名（如果是在线账户，则是邮箱；如果是本地账户，则是本地用户名）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-46-34.png&quot; alt=&quot;检查名称&quot; /&gt;&lt;br /&gt;
▲ 检查名称（点击之后会显示自己的名称）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-48-17.png&quot; alt=&quot;替换子容器和对象的所有者&quot; /&gt;&lt;br /&gt;
▲ 确定之后记得勾选“替换子容器和对象的所有者”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-49-29.png&quot; alt=&quot;确定&quot; /&gt;&lt;br /&gt;
▲ 确定以关掉这个对话框&lt;/p&gt;

&lt;p&gt;于是就会有一个等待窗口，等待即可：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-58-56.png&quot; alt=&quot;更改所有权&quot; /&gt;&lt;br /&gt;
▲ 更改所有权&lt;/p&gt;

&lt;h3 id=&quot;第三步更改权限&quot;&gt;第三步：更改权限&lt;/h3&gt;

&lt;p&gt;这时再次点击高级，“高级安全设置”对话框中的“更改权限”按钮可以点了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-51-16.png&quot; alt=&quot;更改权限&quot; /&gt;&lt;br /&gt;
▲ 更改权限&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-52-47.png&quot; alt=&quot;添加权限&quot; /&gt;&lt;br /&gt;
▲ 现在可以添加权限了&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-53-35.png&quot; alt=&quot;选择主体&quot; /&gt;&lt;br /&gt;
▲ 选择主体&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-46-34.png&quot; alt=&quot;检查名称&quot; /&gt;&lt;br /&gt;
▲ 用同样的方式检查名称&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-55-20.png&quot; alt=&quot;完全控制&quot; /&gt;&lt;br /&gt;
▲ 完全控制&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-55-54.png&quot; alt=&quot;查看已添加的账号&quot; /&gt;&lt;br /&gt;
▲ 发现自己已被添加&lt;/p&gt;

&lt;p&gt;一路点击确认，就设置好啦：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-56-33.png&quot; alt=&quot;设置安全信息&quot; /&gt;&lt;br /&gt;
▲ 设置安全信息&lt;/p&gt;

&lt;h3 id=&quot;享受成果&quot;&gt;享受成果&lt;/h3&gt;

&lt;p&gt;现在删除，即可完成！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-28-00-02-59.png&quot; alt=&quot;删除&quot; /&gt;&lt;br /&gt;
▲ 删除&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Feb 2018 16:03:43 +0000</pubDate>
        <link>https://blog.walterlv.com/post/how-to-delete-windows-apps-folder.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/how-to-delete-windows-apps-folder.html</guid>
        
        <category>Windows10</category>
        
        <category>Win10</category>
        
        
        <category>windows</category>
        
        <category>sysprep</category>
        
      </item>
    
      <item>
        <title>Windows 10 自带那么多图标，去哪里找呢？</title>
        <description>&lt;p&gt;无意间发现我的 D 盘根目录中大部分的文件夹都是系统专用文件夹，有自己的独特图标，偶有一两个开发用的文件夹是默认图标。于是想把它们改成独特样式，&lt;strong&gt;而且是 Windows 10 那些新图标样式&lt;/strong&gt;！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;这是我的文件夹，我希望把最上面几个文件夹的图标改成下面那些风格。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-21-50-42.png&quot; alt=&quot;我的文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;大家都知道在文件夹上右键，选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;属性&lt;/code&gt; → &lt;code class=&quot;highlighter-rouge&quot;&gt;自定义&lt;/code&gt; → &lt;code class=&quot;highlighter-rouge&quot;&gt;更改图标&lt;/code&gt;，这里可以选择很多图标，但用了很多年看腻了，Windows 10 中还自带有那么多，它们又在哪里呢？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-21-53-16.png&quot; alt=&quot;属性 → 自定义 → 更改图标&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Windows 10 自带的图标几乎都在 &lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\*.dll&lt;/code&gt; 中，主要是这些：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Windows 10 风格
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\ddores.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\dmdskres.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\imageres.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\mmres.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\networkexplorer.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\pnidui.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\sensorscpl.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\setupapi.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\shell32.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\wmploc.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot%\system32\wpdshext.dll&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 7 风格
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\accessibilitycpl.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\dsuiext.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\gameux.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\ieframe.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\mstscax.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\netcenter.dll&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows 早期风格
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\compstui.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\mmcndmgr.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\moricons.dll&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%systemroot\system32\pifmgr.dll&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们一起来看看它们都是什么样的吧！&lt;/p&gt;

&lt;h3 id=&quot;windows-10-风格&quot;&gt;Windows 10 风格&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-00-05.png&quot; alt=&quot;ddores.dll&quot; /&gt;&lt;br /&gt;
▲ ddores.dll 包含各种硬件图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-51-23.png&quot; alt=&quot;dmdskres.dll&quot; /&gt;&lt;br /&gt;
▲ dmdskres.dll 磁盘管理所用图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-19-06.png&quot; alt=&quot;imageres.dll&quot; /&gt;&lt;br /&gt;
▲ imageres.dll 各种各样 Windows 10 风格的图标，涵盖各种用途&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-20-21.png&quot; alt=&quot;mmres.dll&quot; /&gt;&lt;br /&gt;
▲ mmres.dll 音频设备图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-30-42.png&quot; alt=&quot;networkexplorer.dll&quot; /&gt;&lt;br /&gt;
▲ networkexplorer.dll 网络和共享中心图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-41-54.png&quot; alt=&quot;pnidui.dll&quot; /&gt;&lt;br /&gt;
▲ pnidui.dll 不要被这些空白迷惑了，这都是白色的网络指示图标（有线、无线、飞行模式等）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-44-03.png&quot; alt=&quot;sensorscpl.dll&quot; /&gt;&lt;br /&gt;
▲ sensorscpl.dll 各种传感器图标（如温度、亮度、声音、指纹、地理位置等）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-45-48.png&quot; alt=&quot;setupapi.dll&quot; /&gt;&lt;br /&gt;
▲ setupapi.dll 为各种硬件安装程序提供的图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-31-42.png&quot; alt=&quot;shell32.dll&quot; /&gt;&lt;br /&gt;
▲ shell32.dll 这个是点开“更改图标”按钮后查看的默认图标库，也包含各种各样 Windows 10 风格的图标，涵盖各种用途&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-47-30.png&quot; alt=&quot;wmploc.dll&quot; /&gt;&lt;br /&gt;
▲ wmploc.dll 各种媒体设备、媒体文件、媒体文件夹&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-48-40.png&quot; alt=&quot;wpdshext.dll&quot; /&gt;&lt;br /&gt;
▲ wpdshext.dll&lt;/p&gt;

&lt;h3 id=&quot;windows-7vista-风格&quot;&gt;Windows 7/Vista 风格&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-21-56-53.png&quot; alt=&quot;accessibilitycpl.dll&quot; /&gt;&lt;br /&gt;
▲ accessibilitycpl.dll 辅助功能图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-52-08.png&quot; alt=&quot;dsuiext.dll&quot; /&gt;&lt;br /&gt;
▲ dsuiext.dll 服务器或网络服务所用图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-18-05.png&quot; alt=&quot;gameux.dll&quot; /&gt;&lt;br /&gt;
▲ gameux.dll 游戏图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-50-34.png&quot; alt=&quot;ieframe.dll&quot; /&gt;&lt;br /&gt;
▲ ieframe.dll IE 所用的图标（部分图标其实已经更新成 Windows 10 风格，给 Edge 用）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-52-53.png&quot; alt=&quot;mstscax.dll&quot; /&gt;&lt;br /&gt;
▲ mstscax.dll 远程桌面连接所用图标（部分图标其实已经更新成 Windows 10 风格）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-29-57.png&quot; alt=&quot;netcenter.dll&quot; /&gt;&lt;br /&gt;
▲ netcenter.dll Windows 7 风格的网络和共享中心所用图标&lt;/p&gt;

&lt;h3 id=&quot;windows-xp20009x3x-风格&quot;&gt;Windows XP/2000/9X/3.X 风格&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-49-36.png&quot; alt=&quot;compstui.dll&quot; /&gt;&lt;br /&gt;
▲ compstui.dll&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-19-40.png&quot; alt=&quot;mmcndmgr.dll&quot; /&gt;&lt;br /&gt;
▲ mmcndmgr.dll 古老的图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-20-54.png&quot; alt=&quot;moricons.dll&quot; /&gt;&lt;br /&gt;
▲ moricons.dll 古老的图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-23-06-22.png&quot; alt=&quot;netshell.dll&quot; /&gt;&lt;br /&gt;
▲ netshell.dll 古老的网络连接图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-31-12.png&quot; alt=&quot;pifmgr.dll&quot; /&gt;&lt;br /&gt;
▲ pifmgr.dll Windows 95 时代古老的图标&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-27-22-54-05.png&quot; alt=&quot;wiashext.dll&quot; /&gt;&lt;br /&gt;
▲ wiashext.dll 各种图片、照片和媒体设备图标&lt;/p&gt;

&lt;p&gt;一个说明：你会发现有些图标是空白的，这个不是 BUG，是微软的无奈……因为有些古老的不负责任的程序会依赖于这些老旧的被微软淘汰的图标，如果微软删掉了这些图标，那么这些程序会崩溃。哎……&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Feb 2018 15:11:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/where-is-the-windows-10-native-icons.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/where-is-the-windows-10-native-icons.html</guid>
        
        <category>Windows10</category>
        
        <category>Win10</category>
        
        
        <category>windows</category>
        
        <category>personalize</category>
        
      </item>
    
      <item>
        <title>制作 Windows 10 安装盘，解决大于 4GB 的 Windows 10 镜像在 UEFI 模式下的安装问题</title>
        <description>&lt;p&gt;制作一个 Windows 安装 U 盘是很容易的，使用 UltraISO 这样的刻录工具量产一个 iso 镜像文件到 U 盘即可。然而随着 Windows 10 版本号的提升，镜像变得越来越大，终于 FAT32 文件系统不再能够容纳得下安装镜像文件 install.wim 了。&lt;/p&gt;

&lt;p&gt;本文将介绍如何制作镜像文件大于 4GB 的 UEFI 启动的系统安装盘。&lt;/p&gt;

&lt;hr /&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;p&gt;充分利用 Windows 8 和 Windows 10 中的特性——“快速启动”，我们必须使用 UEFI 模式启动操作系统；这就要求我们制作的 U 盘安装盘必须以 UEFI 模式启动；这就要求 U 盘文件系统必须是 FAT32 的；这就要求我们的系统镜像文件 install.wim 不大于 4GB。然而 Windows 10 1709 的镜像文件就是大于 4GB，于是悲剧诞生……&lt;/p&gt;

&lt;p&gt;如果你熟悉如何制作 U 盘安装盘，那么可直接从第二步开始阅读；如果不了解，就直接开始吧！&lt;/p&gt;

&lt;h2 id=&quot;第一步下载-windows-10-iso-镜像文件&quot;&gt;第一步：下载 Windows 10 iso 镜像文件&lt;/h2&gt;

&lt;p&gt;微软一般不提供 Windows 10 的下载镜像，但 MSDN I Tell You 收集了几乎所有的 Windows 10 正式版本镜像文件，所以可以 &lt;a href=&quot;https://msdn.itellyou.cn/&quot;&gt;前往 MSDN I Tell You 下载&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-20-45-09.png&quot; alt=&quot;Windows 10 1709 镜像下载地址&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二步使用镜像文件制作安装-u-盘&quot;&gt;第二步：使用镜像文件制作安装 U 盘&lt;/h2&gt;

&lt;p&gt;曾经我一直使用 UltraISO 来制作启动 U 盘，毕竟是老牌刻录软件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-20-50-17.png&quot; alt=&quot;UltraISO&quot; /&gt;&lt;/p&gt;

&lt;p&gt;无论刻录的时候选择了什么样的配置，刻录完之后 U 盘文件系统都会是 FAT32 格式。直到 Windows 10 的前一两个版本，install.wim 都没有超过 4GB，所以我一直以为微软会刻意避免让镜像文件超过 4GB；于是我依然使用它来制作安装盘。然而没有想到的是，当真的超过了 4GB 后，整个刻录过程居然没有报错（虽然事实上证明会安装失败）。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/faq-in-installing-windows.md&quot;&gt;为什么 UEFI 方式启动的 U 盘必须使用 FAT32 文件系统？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可能因为 UltraISO 太老了，以至于都没有看到对大尺寸镜像文件的支持。于是，我招到了另一款——rufus：&lt;/p&gt;

&lt;h3 id=&quot;推荐使用开源软件-rufus&quot;&gt;推荐使用开源软件 rufus&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rufus.akeo.ie/&quot;&gt;rufus 官网&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/pbatard/rufus&quot;&gt;rufus 的 GitHub 仓库&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;官方对它的广告词是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Reliable USB Formatting Utility&lt;br /&gt;
靠谱的 U 盘格式化工具&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;启动后就只有一个设置界面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-21-19-56.png&quot; alt=&quot;设置界面&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对我们至关重要的选项就是分区方案和目标系统类型（Partition scheme and target system type）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-21-20-40.png&quot; alt=&quot;分区方案和目标系统类型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这意味着我们量产后的 U 盘将支持 UEFI 启动，同时支持 GPT 分区。这样，我们便能够以 UEFI 的方式启动 U 盘。&lt;/p&gt;

&lt;p&gt;另一个选项是文件系统（File system）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-21-24-24.png&quot; alt=&quot;文件系统&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于 Windows 10 的系统镜像大于 4GB，所以我们需要选择 NTFS（exFAT 也行，但此文件系统不太成熟）。&lt;/p&gt;

&lt;p&gt;其他保持默认即可，或者按照我图中所选。记得点击此处选择要使用的镜像 iso 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-21-26-39.png&quot; alt=&quot;选择 iso 文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“开始”后静待进度条结束，我们便得到了一个可以 UEFI 启动的 Windows 安装 U 盘。&lt;/p&gt;

&lt;h3 id=&quot;观察-rufus-制作的-u-盘&quot;&gt;观察 rufus 制作的 U 盘&lt;/h3&gt;

&lt;p&gt;这不是安装过程中必要的步骤，只是为了满足好奇心。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-22-18-55.png&quot; alt=&quot;rufus 制作的安装盘&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，rufus 实际做了这些事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将 U 盘所有内容清除，并转换成 GPT 格式（更多转换信息可阅读我的&lt;a href=&quot;/post/convert-mbr-to-gpt-during-windows-installation.md&quot;&gt;另一篇博客&lt;/a&gt;）。&lt;/li&gt;
  &lt;li&gt;将 U 盘分成两个区，一个 FAT，包含用于在 EFI 下加载 NTFS 文件系统所必须的组件；一个 NTFS，包含安装 Windows 所需的真正文件（4GB 的镜像不在话下）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-22-24-32.png&quot; alt=&quot;4GB 的 install.wim&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三步重启电脑并选择-uefi-u-盘启动&quot;&gt;第三步：重启电脑并选择 UEFI U 盘启动&lt;/h2&gt;

&lt;h3 id=&quot;在-windows-系统中&quot;&gt;在 Windows 系统中&lt;/h3&gt;

&lt;p&gt;按住 Shift，然后点击“重启”按钮，Windows 10 将会在重启后进入 RE 环境：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-21-32-52.png&quot; alt=&quot;Shift + 重启&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 RE 环境中选择使用可移动存储设备启动即可使用 U 盘启动。&lt;/p&gt;

&lt;h3 id=&quot;使用更传统的方案&quot;&gt;使用更传统的方案&lt;/h3&gt;

&lt;p&gt;当然，大部分主板都支持开机期间按下 F12 来临时选择启动设备。不过，如果在主板上开启了“快速启动”，那么很有可能根本就来不及按下 F12！这时可以采用上面的方案。&lt;/p&gt;

&lt;p&gt;选择带 UEFI 前缀的 U 盘。&lt;/p&gt;

&lt;p&gt;不管使用哪一种方案，启动后将看到此时启动的 U 盘会提示正在加载 NTFS EFI loader：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-02-22-22-22-30.png&quot; alt=&quot;NTFS EFI loader&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第四步选择-gpt-分区的驱动器并按套路安装-windows&quot;&gt;第四步：选择 GPT 分区的驱动器，并按套路安装 Windows&lt;/h2&gt;

&lt;p&gt;在安装界面中，我们需要确保选择的驱动器是 GPT 分区的，因为 UEFI 启动时不支持 MBR 分区表。&lt;/p&gt;

&lt;p&gt;如果没有驱动器是 GPT 分区的，该怎么办？可以使用命令转换一个 MBR 分区的驱动器到 GPT 分区。参见 &lt;a href=&quot;/post/convert-mbr-to-gpt-during-windows-installation.md&quot;&gt;在 Windows 安装期间将 MBR 驱动器转换为 GPT 驱动器&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;接下来，一路下一步并略加设置即可。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/pbatard/rufus/issues/589&quot;&gt;Creating Windows 10 UEFI fat32 USB Stick from NTFS Windows 10 ISO not possible · Issue #589 · pbatard/rufus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 22 Feb 2018 14:14:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/create-bootable-usb-drive-with-wim-file-larger-than-4gb.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/create-bootable-usb-drive-with-wim-file-larger-than-4gb.html</guid>
        
        <category>U盘</category>
        
        <category>优盘</category>
        
        <category>量产</category>
        
        <category>Windows10</category>
        
        <category>Win10</category>
        
        <category>4GB</category>
        
        <category>FAT32</category>
        
        <category>NTFS</category>
        
        <category>GPT</category>
        
        <category>UEFI</category>
        
        
        <category>windows</category>
        
        <category>sysprep</category>
        
      </item>
    
      <item>
        <title>在 Windows 安装期间将 MBR 磁盘转换为 GPT 磁盘</title>
        <description>&lt;p&gt;以 UEFI 启动的 Windows 磁盘必须是 GPT 格式。本文将介绍如何在安装 Windows 期间将磁盘从 MBR 转换成 GPT。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;特别注意&lt;/strong&gt;：操作不慎可能丢失所有数据，如果你懂得安装系统的一些基本概念，那么可以继续阅读并尝试实操；否则请交给专业人士操作。&lt;strong&gt;切记，切记，切记！！！&lt;/strong&gt;&lt;/p&gt;

&lt;div id=&quot;toc&quot;&gt;&lt;/div&gt;

&lt;h2 id=&quot;第一步按下-shift--f10-启动命令提示符&quot;&gt;第一步：按下 Shift + F10 启动命令提示符&lt;/h2&gt;

&lt;p&gt;在 Windows 的安装界面其实是可以启动命令提示符的，只需按下 Shift + F10 即可。&lt;/p&gt;

&lt;h2 id=&quot;第二步敲命令&quot;&gt;第二步：敲命令&lt;/h2&gt;

&lt;p&gt;启动 Diskpart：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;diskpart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动后提示符的前面会出现“DISKPART”前缀：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时命令提示符中会列出此计算机上所有的磁盘和其格式：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;###  状态           大小     可用     Dyn  Gpt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;119&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;118&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;465&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;磁盘&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;联机&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;磁盘 0 是我准备装系统的系统盘。&lt;/p&gt;

&lt;p&gt;================ ！！！&lt;strong&gt;特别注意&lt;/strong&gt;！！！ ================&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;注意这里选择的是磁盘，而&lt;strong&gt;不是分区&lt;/strong&gt;！&lt;strong&gt;不是&lt;/strong&gt;通常所说的 C 盘/D 盘，而是一块 SSD，或一块机械硬盘。&lt;/li&gt;
  &lt;li&gt;后续操作会&lt;strong&gt;清除选中磁盘中的所有数据&lt;/strong&gt;，是所有数据，毫无保留！&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;================ ！！！&lt;strong&gt;特别注意&lt;/strong&gt;！！！ ================&lt;/p&gt;

&lt;p&gt;现在，我们选中“磁盘 0”：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的序号取决于安装 SSD 或机械硬盘的的连接线序号，所以一定要仔细查清楚，不要选错了。&lt;/p&gt;

&lt;p&gt;接着，敲入 &lt;code class=&quot;highlighter-rouge&quot;&gt;clean&lt;/code&gt; 命令清除此磁盘上的所有内容，&lt;strong&gt;注意，这包括了所有的分区&lt;/strong&gt;：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等待清除结束，然后敲入 &lt;code class=&quot;highlighter-rouge&quot;&gt;convert gpt&lt;/code&gt; 命令完成转换。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gpt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;第三步平复激动的心情&quot;&gt;第三步：平复激动的心情&lt;/h2&gt;

&lt;p&gt;操作结束之后直接按下 Alt + F4 切换到 Windows 安装程序继续安装即可。如果你是强迫症重度患者，敲一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;exit&lt;/code&gt; 命令结束“Diskpart”程序也未尝不可。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;DISKPART&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 22 Feb 2018 14:13:16 +0000</pubDate>
        <link>https://blog.walterlv.com/post/convert-mbr-to-gpt-during-windows-installation.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/convert-mbr-to-gpt-during-windows-installation.html</guid>
        
        <category>MBR</category>
        
        <category>GPT</category>
        
        
        <category>windows</category>
        
        <category>sysprep</category>
        
      </item>
    
      <item>
        <title>安装 Windows 需要知道的 256 个问题</title>
        <description>&lt;p&gt;如果你希望更刺激地安装 Windows，那么你需要了解很多 Windows 系统相关的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;为什么-uefi-方式启动的-u-盘必须使用-fat32-文件系统&quot;&gt;为什么 UEFI 方式启动的 U 盘必须使用 FAT32 文件系统？&lt;/h2&gt;

&lt;p&gt;因为 NTFS 是 Windows 系统专属的文件系统，而 UEFI 目前并不支持 NTFS。&lt;/p&gt;

&lt;p&gt;于是，如果在主板设置中选择“仅 UEFI 启动”，那么 NTFS 格式的启动 U 盘在 F12 启动选项中将不可见；而如果设置为“UEFI 启动并兼容旧模式”，那么虽可以在 F12 启动选项中看得见启动 U 盘，但选择启动后是普通的 BIOS 启动。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：在 F12 的启动选项中，UEFI 启动的选项会有一个前缀“UEFI: ”。&lt;/p&gt;
</description>
        <pubDate>Thu, 22 Feb 2018 12:57:12 +0000</pubDate>
        <link>https://blog.walterlv.com/post/faq-in-installing-windows.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/faq-in-installing-windows.html</guid>
        
        <category>Windows10</category>
        
        <category>Win10</category>
        
        <category>FAT32</category>
        
        <category>NTFS</category>
        
        <category>GPT</category>
        
        <category>UEFI</category>
        
        
        <category>windows</category>
        
        <category>sysprep</category>
        
      </item>
    
      <item>
        <title>WPF 和 UWP 中，不用设置 From 或 To，Storyboard 即拥有更灵活的动画控制</title>
        <description>&lt;p&gt;无论是 WPF 还是 UWP 开发，如果用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Storyboard&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Animation&lt;/code&gt; 做动画，我们多数时候都会设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 属性，用于从起始值动画到目标值。然而动画并不总是可以静态地指定这些值，因为更多的时候动画的起始值和目标值取决于当前 UI 的状态。&lt;/p&gt;

&lt;p&gt;本文中，我将将尽量避免设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 值，让动画可以随时中断并重新开始，而中途不会出现突兀的变化。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文涉及到的代码均在 GitHub 上以 MIT License 开源：&lt;a href=&quot;https://github.com/walterlv/sharing-demo/tree/demo/storyboard-without-using-from-or-to&quot;&gt;walterlv/sharing-demo at demo/storyboard-without-using-from-or-to&lt;/a&gt;。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;预览效果&quot;&gt;预览效果&lt;/h2&gt;

&lt;p&gt;下面是本文期望实现的基本效果：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在 WPF 中的动画效果&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-26-wpf-move-to-randomly.gif&quot; alt=&quot;WPF 动画随机移动&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;在 UWP 中的动画效果&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-26-uwp-move-to-randomly.gif&quot; alt=&quot;UWP 动画随机移动&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;预备代码&quot;&gt;预备代码&lt;/h2&gt;

&lt;p&gt;为了让读者能够最快速地搭建一个可供试验的 DEMO，我这里贴出界面部分核心代码。&lt;/p&gt;

&lt;p&gt;XAML 是这样的（这里的 XAML，WPF 和 UWP 完全一样，可以互相使用而不用修改任何代码）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;布局部分&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.RowDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RowDefinition&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Auto&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RowDefinition/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.RowDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ColumnDefinition/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid.ColumnDefinitions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;平移至随机位置&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeginStoryboard_Click&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;从随机位置平移&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BeginStoryboard2_Click&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;暂停&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PauseStoryboard_Click&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisplayCanvas&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisplayShape&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ForestGreen&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;UIElement.RenderTransform&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;X=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Y=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UIElement.RenderTransform&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Rectangle&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;资源部分&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Page.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CircleEase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EasingFunction.Translate&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EaseOut&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 为了方便使用，在 UWP 中加上了 x:Name；WPF 代码请删除 x:Name --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TranslateStoryboard&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.Translate&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.Translate}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Y&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.Translate}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Page.Resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;.xaml.cs 文件中预备一些属性和字段方便使用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#if !WINDOWS_UWP
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 因为 WPF 不能在资源中指定 x:Name，所以需要在后台代码中手动查找动画资源。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.Translate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateXAnimation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateYAnimation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Random&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ticks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areaX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areaY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;areaX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;areaY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;探索动画&quot;&gt;探索动画&lt;/h2&gt;

&lt;p&gt;由于我们期望元素从当前所在的位置开始动画，到我们指定的另一个随机位置，所以直接在 XAML 中指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 是一个艰难的行为。我们只好在 .xaml.cs 文件中指定。&lt;/p&gt;

&lt;h3 id=&quot;wpf&quot;&gt;WPF&lt;/h3&gt;

&lt;p&gt;在 WPF 中，如果我们没有指定动画的 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt;，那么动画将从当前值开始；如果我们没有指定动画的 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt;，那么动画将到当前值结束。从这个角度上说，似乎不设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 将导致动画保持在当前值不变，不会有动画效果。&lt;/p&gt;

&lt;p&gt;但是，WPF 允许在动画进行中修改动画参数，于是我们可以直接开始动画，然后再动画进行中修改元素属性到目标值。&lt;/p&gt;

&lt;p&gt;也就是说，可以这么写：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginStoryboard_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;快速点击这个按钮看看，你会发现每次点击都可以立即从当前位置开始向新的目标位置动画。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-26-wpf-move-quickly.gif&quot; alt=&quot;快速点击动画&quot; /&gt;&lt;/p&gt;

&lt;p&gt;不过你应该注意到了一个坑——第一次并没有播放动画，而是直接跳到了目标位置；这是因为动画还没有保持住元素的位置。我们需要在初始化的时候播放一次动画；&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样就解决了第一次动画不播放的问题。&lt;/p&gt;

&lt;p&gt;现在，我们加上暂停按钮：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PauseStoryboard_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;即便是中途有暂停，依然能够继续让动画朝新的目标位置动画。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-26-wpf-move-with-pause.gif&quot; alt=&quot;快速点击动画&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果我们希望动画从一个新的起点开始，而不是从当前状态开始，则只需要在动画开始之前设置元素的位置即可：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginStoryboard2_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;uwp&quot;&gt;UWP&lt;/h3&gt;

&lt;p&gt;UWP 的情况就不如 WPF 那么灵活了。在 UWP 中，如果不给动画指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 值，那么动画根本就会直接朝 &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; 位置执行。&lt;/p&gt;

&lt;p&gt;于是在动画执行之前，设置动画的 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 值不可避免：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginStoryboard_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;AnimateToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uwp_AnimateToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateXAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateYAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这样的写法下，灵活性与 WPF 相当，但 WPF 中支持在动画没有播放的时候随时设置元素位置，而这种方式则不行（其值会被动画保持）。&lt;/p&gt;

&lt;h2 id=&quot;完整的后台代码&quot;&gt;完整的后台代码&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoryboardPage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Page&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StoryboardPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#if !WINDOWS_UWP
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FindResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.Translate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateXAnimation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateYAnimation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Random&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ticks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Loaded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginStoryboard_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Uwp_AnimateToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BeginStoryboard2_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Uwp_AnimateToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PauseStoryboard_Click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Conditional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WINDOWS_UWP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uwp_AnimateToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateXAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateYAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Conditional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WPF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveToRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NextRandomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areaX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areaY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;areaX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;areaY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在 WPF 中，可以不通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 来指定动画的起始值和终止值；但如果真的不指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt;，需要提前播放一次动画以确保动画能保持住元素状态；&lt;/li&gt;
  &lt;li&gt;在 WPF 中，如果没有指定 &lt;code class=&quot;highlighter-rouge&quot;&gt;From&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt;，那么动画结束后依然能直接为元素属性复制，且会立刻生效（正常情况下需要先清除动画）；&lt;/li&gt;
  &lt;li&gt;在 UWP 中，必须指定动画的 &lt;code class=&quot;highlighter-rouge&quot;&gt;To&lt;/code&gt; 才能按照期望播放到目标值。&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Mon, 19 Feb 2018 22:41:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/using-storyboard-without-from-or-to.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/using-storyboard-without-from-or-to.html</guid>
        
        <category>Storyboard</category>
        
        <category>Animation</category>
        
        <category>From</category>
        
        <category>To</category>
        
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>分享一个算法，计算能在任何背景色上清晰显示的前景色</title>
        <description>&lt;p&gt;背景色千差万别，如果希望在这样复杂的背景色下显示清晰可辨的前景色（例如显示文字），那如何选择这样的前景色才能确保适用于所有的背景呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;灰度图的心理学公式&quot;&gt;灰度图的心理学公式&lt;/h2&gt;

&lt;p&gt;红绿蓝三色是非常不直观的颜色表示的方法，如果不经过训练，人类几乎没有办法直接通过 RGB 的值来猜出大概的颜色来。而 HSB 是用来解决人眼感知问题的，它将颜色用色相、饱和度、明度来表示。&lt;/p&gt;

&lt;p&gt;可是，即便是 HSB 也不能完美解决人眼的感知问题。看下图，黄色和蓝色的饱和度和明度一样，只是色相不同，你觉得哪一个颜色更亮，哪一个更暗？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-04-22-18-36.png&quot; alt=&quot;黄色和蓝色的感知亮度对比&quot; /&gt;&lt;/p&gt;

&lt;p&gt;相信大家都会觉得黄色更亮，蓝色总给人一种阴暗的感觉。&lt;/p&gt;

&lt;p&gt;所以，在饱和度和明度之外，一定还有一种人眼对亮度的感觉是与色相相关的。&lt;/p&gt;

&lt;p&gt;我们将不同色相的颜色排成一圈，观察下哪些颜色更亮，哪些更暗：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-04-22-16-35.png&quot; alt=&quot;相同饱和度明度下的不同色相&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们将上面的不同颜色直接转成灰度图像，这是最能反映人眼感知的灰度图像，它将是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-04-22-29-08.png&quot; alt=&quot;不同色相给人的心里感知亮度&quot; /&gt;&lt;/p&gt;

&lt;p&gt;也就是说，不同的颜色值总能找到一个人眼感知的灰度值，这是著名的心理学公式：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;灰度 = 红×0.299 + 绿×0.587 + 蓝×0.114&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;在灰度背景色上决定前景色&quot;&gt;在灰度背景色上决定前景色&lt;/h2&gt;

&lt;p&gt;一个图像的每一个像素经过上面的公式计算得到的新的图像，即是人眼感知亮度的灰度图。&lt;/p&gt;

&lt;p&gt;于是，当我们期望计算一个能在背景色上清晰显示的前景色时，我们可将背景颜色转换为灰度颜色，然后根据灰度程度，选取黑色或白色作为前景色。&lt;/p&gt;

&lt;p&gt;当然，如果你喜欢，可以将一段黑色或接近于黑色的灰度色作为浅色背景的前景；将一段白色或颉俊宇白色的灰度色作为深色背景的前景。&lt;/p&gt;

&lt;h2 id=&quot;代码实现&quot;&gt;代码实现&lt;/h2&gt;

&lt;p&gt;为了实现这个效果，我们先写一个灰度/亮度的计算函数：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 获取一个颜色的人眼感知亮度，并以 0~1 之间的小数表示。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetGrayLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.299&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.587&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.114&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后写一个根据感知亮度计算反色的方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetReverseForegroundColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grayLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grayLevel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Black&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，当我们希望计算某个背景色上一定能清晰显示的前景色时，只需要调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;GetReverseForegroundColor&lt;/code&gt; 即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-04-22-36-30.png&quot; alt=&quot;测试 ForestGreen 颜色&quot; /&gt;
&lt;img src=&quot;/static/posts/2017-11-04-22-42-45.png&quot; alt=&quot;测试 Teal 颜色&quot; /&gt;
&lt;img src=&quot;/static/posts/2017-11-04-22-43-28.png&quot; alt=&quot;测试 YellowGreen 颜色&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;我封装的方便的-api&quot;&gt;我封装的方便的 API&lt;/h2&gt;

&lt;p&gt;不过，总是写后台代码来计算，对于 XAML 类的程序来说还是麻烦了些，于是我写了一些用于 XAML 的标记扩展，方便让一些文字自动根据背景色改变颜色。&lt;/p&gt;

&lt;p&gt;这是期望的最简用法：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{media:LuminanceForeground}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;我是前景 by walterlv&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为内部已经使用绑定来实现动态变化，所以，无需在颜色更改时再次更新：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-05-reversing-background-to-foreground.gif&quot; alt=&quot;支持动态的背景色&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于这份封装的 API 目前还在完善中，会经常改动，所以只贴出 GitHub 仓库地址，不放在这里：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Media/LuminanceForegroundExtension.cs&quot;&gt;LuminanceForegroundExtension&lt;/a&gt; 写出此用法的关键类&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Media/LuminanceReverseColor.cs&quot;&gt;LuminanceReverseColor&lt;/a&gt; 包含亮度灰度值反色的逻辑&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/walterlv/sharing-demo/blob/master/src/Walterlv.Demo.WPF/Utils/Xaml/DependencyMarkupExtension.cs&quot;&gt;DependencyMarkupExtension&lt;/a&gt; 给标记扩展中一些恶心的代码提供封装&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Luma_(video)&quot;&gt;Luma (video) - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/carekee/articles/3629964.html&quot;&gt;从RGB色转为灰度色算法（转） - carekee - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 19 Feb 2018 22:37:19 +0000</pubDate>
        <link>https://blog.walterlv.com/post/get-gray-reversed-color.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/get-gray-reversed-color.html</guid>
        
        
        <category>algorithm</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>在 Windows 10 上为 WPF 窗口添加模糊特效（就像开始菜单和操作中心那样）</title>
        <description>&lt;p&gt;其实我是希望能够找到为 Win32 桌面程序实现 Fluent Design System 效果的，不过一直没找到。倒是发现了一个可以让 Win32 桌面程序做出类似 Windows 10 开始菜单和操作中心那种模糊效果的方法。&lt;/p&gt;

&lt;p&gt;写这篇文章并不意味着我推荐大家这么去做，只是希望将方法总结出来，作为一个研究点而已。&lt;/p&gt;

&lt;p&gt;本文提供了一个完整的用于在 Windows 10 上实现模糊特效的 C# 类，没有放到 GitHub 也没有其他类型的开源。如果需要直接拿走就好。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-01-23-47-29.png&quot; alt=&quot;白底效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-01-23-49-15.png&quot; alt=&quot;黑底效果&quot; /&gt;&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;为什么不推荐使用&quot;&gt;为什么不推荐使用？&lt;/h2&gt;

&lt;p&gt;当初 Windows Vista 推出 Aero 特效后惊艳了世人。然而那还是个 30 帧动画大行其道的年代，即便是后来的 Windows 7 也是如此。这个特效不能使用更高帧率就在于对资源的消耗量太感人。然而 Windows 8/8.1 的推出，动画是其中的一个重要部分——那全屏的感人的流畅的动画，那丝般的顺滑，让人难忘。然而这么流畅是有代价的——需要 60 帧满速运行，而且不能占用太多资源，不然依然卡顿。于是微软只好砍掉了背景高斯模糊功能……充满遗憾……被世人唾骂……&lt;/p&gt;

&lt;p&gt;忍受不了世人的咒骂，微软只好再把高斯模糊效果带回 Windows 10。可是，在算法没有从根本上得到改进的情况下，大量的资源消耗依然是不可忽视的问题。所以微软现在只好在少数几个地方先用用，满足大家曾经对于 Aero 的呼声，适当提升一点点审美。&lt;/p&gt;

&lt;p&gt;既然微软能用，那么我们也理应能用。然而事实情况是——微软没有任何文档来说明如何实现这样的效果。足以说明微软也不希望他们担心的性能问题大量出现在用户的电脑上。（对于移动设备如 Surface 来说，带来的就是电池可用时间的缩短。）&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/people/minmin.gong/activities&quot;&gt;叛逆者&lt;/a&gt; 说，他们终于在特效的算法上有了质的突破，创意来源于平时小组言谈中一点点想法。这就是 Fluent Design System！终于只需要非常少量的计算资源就能达到非常炫酷的现代效果。让人印象深刻的可以替代 Aero 的就属亚克力（Acrylic）了。这效果是在 DWM 进程上运行的（与 Aero 特效一样），所以也不会额外占用应用程序本身的计算资源。&lt;/p&gt;

&lt;p&gt;然而，本文探究的方法并不是 Fluent Design System 中的任何部分。依然是微软不期望大家使用的方法，所以，本文并不推荐大家作为真实项目使用，而是作为一种探究学习的途径。&lt;/p&gt;

&lt;h2 id=&quot;我封装的-api&quot;&gt;我封装的 API&lt;/h2&gt;

&lt;p&gt;为了方便大家使用，我封装了一个小的 API。于是大家可以非常方便地使用。&lt;/p&gt;

&lt;p&gt;如果你想在 XAML 里用，直接在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt; 上加上以下两行：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xmlns:interop=&quot;clr-namespace:Walterlv.Demo.Interop&quot;
interop:WindowBlur.IsEnabled=&quot;True&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你希望直接在 cs 文件里面写，则这样就好了：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;WindowBlur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetIsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意这里的 &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt; 指的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainWindow&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;事实上，当你用了上面的 API 试图看一看效果的时候，你会发现其实并不如本文一开始的图片那样。而是一个非常丑陋的窗口：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-02-00-04-29.png&quot; alt=&quot;一开始的丑陋&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你需要做两件事情才能变得好看一些：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;设置窗口背景色为透明（&lt;code class=&quot;highlighter-rouge&quot;&gt;Transparent&lt;/code&gt;）/半透明（&lt;code class=&quot;highlighter-rouge&quot;&gt;#A0FFFFFF&lt;/code&gt;），以便去掉默认的白色背景。&lt;/li&gt;
  &lt;li&gt;为窗口设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 属性，以便去掉标题栏颜色的不同，并修复周围阴影几个像素的半透明偏差。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;完整的代码可以看这里：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:interop=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo.Interop&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Blur Demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;350&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;525&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;interop:WindowBlur.IsEnabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Transparent&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#A0FFFFFF&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt;
                   &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;
                   &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;48&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;白底效果&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;LineBreak/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Run&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv.github.io&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;实现原理setwindowcompositionattribute&quot;&gt;实现原理——SetWindowCompositionAttribute&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowBlur&lt;/code&gt; 类内部用到了微软从未开放的 API，&lt;a href=&quot;https://www.zhihu.com/people/minmin.gong/activities&quot;&gt;叛逆者&lt;/a&gt; 也已经证实这就是微软在开始菜单和操作中心中用到的 API。这个 API 就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;SetWindowCompositionAttribute&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;事实上此类中的代码来源也是多个地方找到的，最开始是 C 语言的版本，而后从 &lt;a href=&quot;https://github.com/Nukepayload2/sample-win10-aeroglass&quot;&gt;Nukepayload2/sample-win10-aeroglass&lt;/a&gt; 找到了 C# 的版本，最终基于它改造成了现在这个样子。&lt;/p&gt;

&lt;p&gt;代码见本文最后，因为我想把参考资料放到前面来，以感谢前人的努力。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/59724483/answer/168191216?utm_medium=social&amp;amp;utm_source=wechat_session&quot;&gt;如何评价微软在 Build 2017 上提出的 Fluent Design System？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app&quot;&gt;windows - Mimicking Acrylic in a Win32 app - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/32724187/how-do-you-set-the-glass-blend-colour-on-windows-10&quot;&gt;winapi - How do you set the glass blend colour on Windows 10? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/ysc3839/article/details/50451064&quot;&gt;调用未公开API SetWindowCompositionAttribute 在Win10下开启Aero - CSDN博客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/54147239&quot;&gt;Windows 10 开始菜单的高斯模糊效果是如何实现的？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/53157092/answer/133772272?utm_source=qq&amp;amp;utm_medium=social&quot;&gt;从编程的角度来说，Windows 的开始菜单是如何实现的？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/yinyue200/p/6623307.html&quot;&gt;Windows 10 Creators Update 新功能——画中画模式和窗口高斯模糊 - yinyue200 - 博客园&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Nukepayload2/sample-win10-aeroglass&quot;&gt;Nukepayload2/sample-win10-aeroglass&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;附封装好的-api-代码&quot;&gt;附：封装好的 API 代码&lt;/h2&gt;

&lt;script src=&quot;https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00.js&quot;&gt;&lt;/script&gt;

</description>
        <pubDate>Mon, 19 Feb 2018 22:31:10 +0000</pubDate>
        <link>https://blog.walterlv.com/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html</guid>
        
        <category>WPF</category>
        
        <category>Windows</category>
        
        <category>Blur</category>
        
        <category>SetWindowCompositionAttribute</category>
        
        
        <category>win10</category>
        
        <category>windows</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>应该抛出什么异常？不应该抛出什么异常？（.NET/C#）</title>
        <description>&lt;p&gt;我在 &lt;a href=&quot;/post/suggestions-for-handling-exceptions&quot;&gt;.NET/C# 建议的异常处理原则&lt;/a&gt; 中描述了如何 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 异常以及重新 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt;。然而何时应该 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 异常，以及应该 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 什么异常呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;究竟是谁错了&quot;&gt;究竟是谁错了？&lt;/h2&gt;

&lt;p&gt;代码中从上到下从里到外都是在执行一个个的包含某种目的的代码，我们将其称之为“任务”。当需要完成某项任务时，任务的完成情况只有两种结果：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;成功完成&lt;/li&gt;
  &lt;li&gt;失败&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;异常处理机制就是处理上面的第 2 种情况。这里我们不谈论错误码系统，那么，异常便应该在任务执行失败时抛出异常。&lt;/p&gt;

&lt;p&gt;抛出异常后，报告错误只是手段，真正要做的是&lt;strong&gt;帮助开发者修复错误&lt;/strong&gt;。于是，第一个要做的就是区分到底——谁错了！&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;任务的使用者用错了&lt;/li&gt;
  &lt;li&gt;任务的执行代码写错了&lt;/li&gt;
  &lt;li&gt;任务执行时所在的环境不符合预期&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;简单说来，就是：使用错误，实现错误、环境错误。&lt;/p&gt;

&lt;h2 id=&quot;让我们把异常归类到这些错误中&quot;&gt;让我们把异常归类到这些错误中&lt;/h2&gt;

&lt;p&gt;本文的重点在于指导我们何时应该抛出什么异常，也就是说——我们的角色是——任务的编写者。那么，编写者有责任编写出一段没有错误的代码。这就说明——&lt;strong&gt;永远不应该抛出表示自己写错了的异常&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;那么，我们对常见的异常进行分类。&lt;/p&gt;

&lt;h3 id=&quot;使用错误&quot;&gt;使用错误&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentException&lt;/code&gt; 表示参数使用错了
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt; 表示参数不应该传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentOutOfRangeException&lt;/code&gt; 表示参数中的序号超出了范围&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidEnumArgumentException&lt;/code&gt; 表示参数中的枚举值不正确&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt; 表示当前状态下不允许进行此操作（也就是说存在着允许进行此操作的另一种状态）
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ObjectDisposedException&lt;/code&gt; 表示对象已经 &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; 过了，不能再使用了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotSupportedException&lt;/code&gt; 表示不支持进行此操作（这是在说不要再试图对这种类型的对象调用此方法了，不支持）
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PlatformNotSupportedException&lt;/code&gt; 表示在此平台下不支持（如果程序跨平台的话）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;实现错误&quot;&gt;实现错误&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; 试图在空引用上执行某些方法，除了告诉实现者出现了意料之外的 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 之外，没有什么其它价值了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IndexOutOfRangeException&lt;/code&gt; 使用索引的时候超出了边界&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidCastException&lt;/code&gt; 表示试图对某个类型进行强转但类型不匹配&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StackOverflow&lt;/code&gt; 表示栈溢出，这通常说明实现代码的时候写了不正确的显式或隐式的递归&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OutOfMemoryException&lt;/code&gt; 表示托管堆中已无法分出期望的内存空间，或程序已经没有更多内存可用了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AccessViolationException&lt;/code&gt; 这说明使用非托管内存时发生了错误&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;BadImageFormatException&lt;/code&gt; 这说明了加载的 dll 并不是期望中的托管 dll&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TypeLoadException&lt;/code&gt; 表示类型初始化的时候发生了错误&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;环境错误&quot;&gt;环境错误&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;IOException&lt;/code&gt; 下的各种子类&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Win32Exception&lt;/code&gt; 下的各种子类&lt;/li&gt;
  &lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;无法归类&quot;&gt;无法归类&lt;/h3&gt;

&lt;p&gt;不应该抛出，却又不得不抛出的异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt; 这只能说明此功能还在开发中，一旦进入正式环境，不要抛出此异常（如果那时真的没有完成，这个方法就应该删除）&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AggregateException&lt;/code&gt; 如果可能，真的不要抛出此异常，因为它本身不包含异常信息，让使用者很难正确 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 这样的异常。如果内部只有一个异常，应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt; 将内部异常合并（请参阅 &lt;a href=&quot;/post/exceptiondispatchinfo-capture-throw&quot;&gt;使用 ExceptionDispatchInfo 捕捉并重新抛出异常 - 吕毅&lt;/a&gt;）（&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 在执行多个任务后，如果多个任务都发生了异常，就抛出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;AggregateException&lt;/code&gt;，但这已经是没有办法的事情了，因为没有办法将两个可能不是同类的异常合并成一个）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;永远都不应该抛出异常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;FormatException&lt;/code&gt; 这算是 .NET 设计上的失误吧……因为当它抛出来时无法准确描述到底什么错了&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationException&lt;/code&gt; 这是各种异常的基类，本身并没有明确的意义&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;SystemException&lt;/code&gt; 这是各种异常的基类，本身并没有明确的意义&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 这可是顶级基类，这都抛出来了，使用者再也无法正确地处理此异常了&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;是时候该决定抛什么异常了&quot;&gt;是时候该决定抛什么异常了&lt;/h2&gt;

&lt;h3 id=&quot;对于使用错误应该在第一时间抛出&quot;&gt;对于使用错误，应该在第一时间抛出&lt;/h3&gt;

&lt;p&gt;既然对方已经用错了，那么代码继续执行也只会错上加错。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_anotherDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;例如上面的方法中使用者传入了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; 参数后，方法必然执行失败 —— 抛出了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;。但是，当拿着这样的异常去调查哪里错了的时候，我们会发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;demo&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;anotherDemo&lt;/code&gt; 都可能为 null。&lt;/p&gt;

&lt;p&gt;然而很明显，这时使用者的错，使用者确保传入的参数不为 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，方法就可以继续执行。&lt;/p&gt;

&lt;p&gt;如果在方法的一开始就抛出使用异常 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt;，那么就可以向使用者报告这样的参数使用错误。&lt;/p&gt;

&lt;p&gt;另外的情况，&lt;code class=&quot;highlighter-rouge&quot;&gt;_anotherDemo&lt;/code&gt; 是此类型中的另一个字段，此时也要求必须非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;。而要确保非 &lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt;，使用者必须使用其它方式隐式初始化这个字段，那么应该抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt;，告诉使用者应该先调用其他的某个方法。&lt;/p&gt;

&lt;p&gt;那么，应该改成：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_anotherDemo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;必须使用 XXX 设置某个值之后才能使用 Foo 方法。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_anotherDemo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，不像 &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt; 通常并不一定能在开始就确定是否满足状态要求，但最好能尽可能在第一时间抛出，避免错误蔓延。&lt;/p&gt;

&lt;p&gt;做到了第一时间抛出使用错误，就能让使用者明确知道自己用错了，需要修改使用代码。&lt;em&gt;（这正是被另外一项事实所逼——典型的程序员是不看文档的，“使用异常”代替了一部分文档。）&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;永远不应该让实现错误抛出&quot;&gt;永远不应该让实现错误抛出&lt;/h3&gt;

&lt;p&gt;这一节的标题其实说了三件事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;永远不应该主动用 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 句式抛出“实现错误”章节中提到的任何异常&lt;/li&gt;
  &lt;li&gt;如果你在调用某个别人实现的代码时遇到了“实现错误”章节中提到的异常，那说明“那个人”的代码写出 BUG 了，确信无疑。&lt;/li&gt;
  &lt;li&gt;如果自己写的代码发现抛出了这些异常，那就说明自己写出了 BUG，需要第一时解决 BUG（是解决，不是逃避）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们假设实现了这段代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Clicked&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果在执行到第一句时发生了 &lt;code class=&quot;highlighter-rouge&quot;&gt;InvalidCastException&lt;/code&gt;，说明实现代码编写是不正确的。&lt;/p&gt;

&lt;p&gt;为了防止发生异常，可能有些人会改成这样：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 请注意：这段示例是错误的！&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Clicked&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是在逃避问题，而不是解决问题！&lt;/p&gt;

&lt;p&gt;这是一段典型的事件处理函数代码，&lt;code class=&quot;highlighter-rouge&quot;&gt;sender&lt;/code&gt; 通常是事件的引发者。写这段代码的人并没有调查 &lt;code class=&quot;highlighter-rouge&quot;&gt;sender&lt;/code&gt; 不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt; 类型的原因，到底是因为在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Grid&lt;/code&gt; 上监听了路由事件的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Click&lt;/code&gt;，还是因为多个控件都把事件处理函数设为了这个方法。如果是前者，这样的改法会让这段代码的全部逻辑失效；如果是后者，这样的改法会让部分逻辑失效。&lt;/p&gt;

&lt;p&gt;更应该去做的，是去检查 &lt;code class=&quot;highlighter-rouge&quot;&gt;+=&lt;/code&gt; 的左边是否乱入了非 &lt;code class=&quot;highlighter-rouge&quot;&gt;Button&lt;/code&gt; 的事件引发者。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnButtonClick&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Click&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnButtonClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;修改这些源头上就已经不正确的代码&lt;/strong&gt;，&lt;strong&gt;才是真正解决问题&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;另一个角度，如果事件的引发者确实可能有多种，那么事件处理函数就应该加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 逻辑，或者不要再使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;sender&lt;/code&gt;，或者强制转换时使用基类型。这也是在真正的解决问题。&lt;/p&gt;

&lt;p&gt;额外的，对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;OutOfMemoryException&lt;/code&gt;，这通常意味着“实现”部分的代码存在着性能问题，应该着手解决。&lt;/p&gt;

&lt;h3 id=&quot;对于环境错误关注于规避和恢复&quot;&gt;对于环境错误，关注于规避和恢复&lt;/h3&gt;

&lt;p&gt;环境错误是难以提前预估的；或者说预估的成本太高，不值得去预估。于是，当发生了环境错误，我们更加关注于&lt;strong&gt;这样的环境中是什么导致了异常&lt;/strong&gt;，以及&lt;strong&gt;程序是否正确处理了这样的异常并恢复错误&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;.NET 中已经为我们准备了很多场景下的多套环境异常，例如 IO 相关的异常，网络连接相关的异常。这些异常都不是我们应该抛出的。&lt;/p&gt;

&lt;h2 id=&quot;程序中的异常&quot;&gt;程序中的异常&lt;/h2&gt;

&lt;p&gt;在异常处理中，每一位开发者应该从根源上在自己的代码中消灭“实现异常”（而不是“逃避”），同时在“使用异常”的帮助下正确调用其他方法，那么代码中将只剩下“环境异常”（和小部分性能导致的“实现异常”）。&lt;/p&gt;

&lt;p&gt;此时，开发者们将有更多的精力关注在“解决的具体业务”上面，而不是不停地解决编码上的 BUG。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;特别的，“实现异常”可以被单元测试进行有效的检测。&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Feb 2018 13:25:51 +0000</pubDate>
        <link>https://blog.walterlv.com/post/throws-which-exception.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/throws-which-exception.html</guid>
        
        
        <category>dotnet</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>.NET/C# 建议的异常处理原则</title>
        <description>&lt;p&gt;“体验”一词早已泛滥却又能够粗略地表达开发团队对客户端产品的要求，“质量”在 &lt;a href=&quot;/post/kano-model&quot;&gt;卡诺模型（KANO Model）&lt;/a&gt; 中是“必备特性”——&lt;strong&gt;做得好了用户感觉不到，做得差一点儿用户就会破口大骂&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;本文将以提升客户端 GUI 产品质量为目标，谈谈 .NET/C# 中建议的异常处理方式。（如果想了解更具体的应该抛出什么异常，请前往我的另一篇文章 &lt;a href=&quot;/post/throws-which-exception&quot;&gt;应该抛出什么异常？ - 吕毅&lt;/a&gt;）&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;不恰当的异常处理会带来什么影响&quot;&gt;不恰当的异常处理会带来什么影响？&lt;/h2&gt;

&lt;p&gt;DEMO 和学习资料是无所谓的，找个地方写写 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 就完了……但是一旦这一行为迁移到了大型产品或软件系统中，不恰当的异常处理将会带来严重的体验下降或者巨大的额外维护成本。&lt;/p&gt;

&lt;h3 id=&quot;严重的体验下降&quot;&gt;严重的体验下降&lt;/h3&gt;

&lt;p&gt;众所周知，如果应用中存在大量未经处理的异常，那么应用分分钟崩溃死掉。如果软件面向最终用户，那么用户将不停地遭遇闪退或者“停止工作”。如果面向服务器或其他系统，频繁挂掉也几乎意味着服务不可用。&lt;/p&gt;

&lt;p&gt;这是异常处理“不足”造成的影响。&lt;/p&gt;

&lt;p&gt;不过，处理“不足”这种情况大家见得少，因为实际开发中更多遇到的不是很多异常未经处理，而是各种异常都处理掉了。算是“过度”吧。&lt;/p&gt;

&lt;h3 id=&quot;巨大的额外维护成本&quot;&gt;巨大的额外维护成本&lt;/h3&gt;

&lt;p&gt;如果我们在各个功能上都加上 &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt;-&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 里吞掉异常，软件确实是不会崩了，但部分功能代码也不会执行执行了。本来用户那里崩溃一下还能逼着开发者去调查一下原因，现在连崩溃都看不到，甚至都不知道软件已经濒临挂掉的边缘。积少成多的这些小错误会瞬间积累，形成一组复杂的不可描述和预知的现象。没有人能说明这现象背后到底是哪个模块的错误导致的。于是，分析一个用户反馈的错误将变得非常低效，每一次错误都难以说出具体出错的模块到底是哪个——软件的质量只有日益下降，维护成本持续升高了。&lt;/p&gt;

&lt;p&gt;不要说在每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 块里记了 log，log 是开发者们从来不会主动去看的文件，从来都是出了问题才看的，而且看了也只能修复 BUG，解决不了问题！&lt;/p&gt;

&lt;p&gt;我举个例子：软件为用户储存一份文档，在此过程中发生了异常却被吞掉了（就算记了 log）；那么用户极有可能得到一份缺失重要内容的损坏的文档——看 log 能帮用户找回损失吗？！&lt;/p&gt;

&lt;h2 id=&quot;总揽全局分层的异常处理&quot;&gt;总揽全局——分层的异常处理&lt;/h2&gt;

&lt;p&gt;异常的处理可以分为四个层：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;任务的执行细节&lt;/li&gt;
  &lt;li&gt;调用任务执行的顶级 UI、顶级命令或包含完整功能的 API&lt;/li&gt;
  &lt;li&gt;线程级别和应用程序域级别&lt;/li&gt;
  &lt;li&gt;驱动模块或应用程序的框架&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-03-10-14-48.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 上图在垂直方向上存在直接调用关系，而在水平方向上是不同时机上的调用&lt;/p&gt;

&lt;p&gt;其中第 4 层并没有出现在上图中，因为它并不能按照执行时机或调用关系来定位，而是可能出现在上图中的任何一处。&lt;/p&gt;

&lt;p&gt;在不同的层上应该做不同的事情，如果每一层都做正确的处理，那么便能够既保留足够的异常信息供开发人员分析，又不会因为异常致使用户用起来感觉软件不稳定。&lt;/p&gt;

&lt;p&gt;接下来，我们将分别说明在每一层应该做些什么，原则是什么。&lt;/p&gt;

&lt;h2 id=&quot;定出原则职责分明&quot;&gt;定出原则——职责分明&lt;/h2&gt;

&lt;h3 id=&quot;执行细节&quot;&gt;执行细节&lt;/h3&gt;

&lt;p&gt;执行细节通常有这些代码：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;组件库/公共组件&lt;/li&gt;
  &lt;li&gt;业务实现代码&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些代码几乎都是要被调用才会开始执行，但在编写时一般较难预见到调用方的使用方式和时机。&lt;/p&gt;

&lt;p&gt;它的异常处理原则是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;提前判断参数和状态，不满足则抛出异常&lt;/strong&gt;&lt;br /&gt;
如果调用方需要提前准备一些状态或参数才能正常执行，那么必须提前判断这些状态；如果判断不通过，需要抛出异常提示调用方需要正确地调用。（如果非私有方法的判断已经足够了，内部的私有方法可以不用再做判断。）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;执行方法承诺的任务，若无法履行承诺，则抛出异常&lt;/strong&gt;&lt;br /&gt;
如果调用的更底层的方法抛出了异常，要么保留这些异常对外抛出（推荐），要么抛出自己的异常并将底层异常包装为内部异常。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;如果异常会导致状态错误或应用程序功能雪崩，需要恢复并重新抛出异常&lt;/strong&gt;&lt;br /&gt;
&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 是用来恢复错误的，而不是用来防止崩溃的。&lt;code class=&quot;highlighter-rouge&quot;&gt;finally&lt;/code&gt; 是用来恢复状态的。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要说明的是，这部分代码通常是一层嵌一层地调用，是每一层都要注意以上原则。&lt;/p&gt;

&lt;h3 id=&quot;顶级-ui命令或-api&quot;&gt;顶级 UI/命令或 API&lt;/h3&gt;

&lt;p&gt;对异常的处理本不应该区分具体的业务实现还是顶级命令或 UI 的，在我试图推荐的异常处理方式中，它也应该遵循前面执行细节里的三项处理原则。但实际在执行的过程中，如果不把顶级命令和 UI 单独拿出来说，会有理解上的困难。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;对顶级 UI 或命令来说，提前判断的参数通常是用户的输入和当前应用程序的若干状态。&lt;br /&gt;
对用户输入来说，提前从交互上防止用户出错是最佳的方式，但也不可避免会存在遗漏，这时肯定不能直接抛个异常给用户；所以此时的最佳处理方案是给出适当的 UI 反馈以告知用户出现的问题和建议的恢复方法。&lt;br /&gt;
对程序当前的状态来说，如果不符合执行某个命令的要求，这个命令应该被禁用并告知用户禁用的原因；而不是执行时抛个异常或者什么都不做。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对顶级 UI 或命令来说，承诺的任务已经开始包含必要的异常处理以及与此处理相关的交互。&lt;br /&gt;
也就是说，&lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 掉&lt;strong&gt;已知的&lt;/strong&gt;几种异常并用友好的 UI 交互形式与用户进行互动也是承诺的一部分。既然承诺的任务能够达成，也不需要抛出异常。（未知原因的异常依然不应该私自处理，因为这依然会导致问题难以定位，何况还是未知异常。）&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;应用程序级别对外公开的 API 考虑到安全性问题，考虑到第三方调用者参差不齐的水平，也会考虑有限地通过 UI 交互来吞掉部分已知的异常。而这时也如以上所说，这些处理也是此 API 承诺任务的一部分。&lt;/p&gt;

&lt;h3 id=&quot;程序统一处理&quot;&gt;程序统一处理&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dispatcher.UnhandledException&lt;/code&gt; 可以处理掉当前 UI 线程上未经处理的异常；&lt;code class=&quot;highlighter-rouge&quot;&gt;AppDomain.UnhandledException&lt;/code&gt; 可以让我们知道当前应用程序域中所有未经处理的异常。&lt;/p&gt;

&lt;p&gt;正是因为统一处理的存在，才使得我们可以放心大胆地在业务代码中抛出能够足够描述当前异常原因的异常而不用担心应用程序会频繁地挂掉。&lt;/p&gt;

&lt;p&gt;不过统一处理的地方能够进行的处理操作有限，比如记个 log 之类，毕竟不知道业务需求。所以并不要指望在统一处理时能够恢复错误，错误还是需要到各个业务方去恢复的。&lt;/p&gt;

&lt;h3 id=&quot;框架&quot;&gt;框架&lt;/h3&gt;

&lt;p&gt;框架代码可能被业务代码调用，也可能调用业务代码。无论哪种，框架从来都不能相信业务代码按照要求和契约来编程。&lt;/p&gt;

&lt;p&gt;处理框架代码被调用时，以正常实现细节被调用的异常处理原则一样即可——确保参数正确，承诺完成并且不完成就抛出异常。&lt;/p&gt;

&lt;p&gt;处理框架调用业务代码时，几乎一定要处理业务代码任何种类崩溃的情况。也就是说，几乎需要恢复错误然后重新抛出异常。&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Feb 2018 13:19:06 +0000</pubDate>
        <link>https://blog.walterlv.com/post/suggestions-for-handling-exceptions.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/suggestions-for-handling-exceptions.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>有些异常堆栈中真没我们写的源码</title>
        <description>&lt;p&gt;有时候会发生一些异常，但异常的堆栈信息中完全找不到我们自己写的源码，这样的异常到底怎么调试！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;本文基于 WPF on .NET Framework 4.5&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;文章一开始，先列出几个异常的前几行。&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.ComponentModel.Win32Exception (0x80004005): Not enough quota is available to process this command.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.Runtime.InteropServices.COMException (0x80070008): Not enough storage is available to process this command. (Exception from HRESULT: 0x80070008)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.Runtime.InteropServices.COMException (0x88980411): Exception from HRESULT: 0x88980411
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;异常堆栈太长了，这里只列出一行，详细的可以看本文文末的“&lt;strong&gt;&lt;a href=&quot;&quot;&gt;附&lt;/a&gt;&lt;/strong&gt;”节，免得影响大家阅读。&lt;/p&gt;

&lt;h2 id=&quot;为什么会发生这些异常&quot;&gt;为什么会发生这些异常？&lt;/h2&gt;

&lt;p&gt;初步看这些异常，几乎都没有什么头绪，因为根本就不是从我们的代码调用中引发的。所以，如果我们不能接触到发生异常的现场的话，几乎就只剩下搜索这一条路线可走了。&lt;/p&gt;

&lt;h3 id=&quot;0x80004005-配额不足无法处理此命令&quot;&gt;0x80004005: 配额不足，无法处理此命令。&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;问题状态：尚未解决。&lt;/li&gt;
  &lt;li&gt;复现步骤：有一种复现方式，疯狂给某个窗口发送 Windows 消息，这个窗口所在的进程可能会收到此错误。&lt;/li&gt;
  &lt;li&gt;表现现象：但从查阅的资料来看，极有可能是导致窗口不渲染（Window Freezes）。&lt;/li&gt;
  &lt;li&gt;猜测原因：窗口用于储存消息队列的容器存储空间耗尽。&lt;br /&gt;
查阅资料：
    &lt;ul&gt;
      &lt;li&gt;Mysterious “Not enough quota is available to process this command” in WinRT port of DataGrid
  &lt;a href=&quot;https://stackoverflow.com/questions/12584619/mysterious-not-enough-quota-is-available-to-process-this-command-in-winrt-port&quot;&gt;https://stackoverflow.com/questions/12584619/mysterious-not-enough-quota-is-available-to-process-this-command-in-winrt-port&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Not enough quota is available to process this command -WPF&lt;br /&gt;
  &lt;a href=&quot;https://stackoverflow.com/questions/20964360/not-enough-quota-is-available-to-process-this-command-wpf&quot;&gt;https://stackoverflow.com/questions/20964360/not-enough-quota-is-available-to-process-this-command-wpf&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;（在查阅此页一天后，此页已无法访问） &lt;br /&gt;
  &lt;a href=&quot;https://support.microsoft.com/en-us/kb/327699&quot;&gt;https://support.microsoft.com/en-us/kb/327699&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Invisible WPF dialog filling up windows message queue?&lt;br /&gt;
  &lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/6e94283a-76be-42b3-98e6-a8e18c4e43de/invisible-wpf-dialog-filling-up-windows-message-queue?forum=wpf&quot;&gt;https://social.msdn.microsoft.com/Forums/vstudio/en-US/6e94283a-76be-42b3-98e6-a8e18c4e43de/invisible-wpf-dialog-filling-up-windows-message-queue?forum=wpf&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Diagnosis on “Quota Exceeded” Win32Exception&lt;br /&gt;
  &lt;a href=&quot;https://stackoverflow.com/questions/10086985/diagnosis-on-quota-exceeded-win32exception&quot;&gt;https://stackoverflow.com/questions/10086985/diagnosis-on-quota-exceeded-win32exception&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;WPF app fails to start second time&lt;br /&gt;
  &lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/664f2de6-342f-4527-977e-a7e12eac8d90/wpf-app-fails-to-start-second-time-?forum=wpf&quot;&gt;https://social.msdn.microsoft.com/Forums/vstudio/en-US/664f2de6-342f-4527-977e-a7e12eac8d90/wpf-app-fails-to-start-second-time-?forum=wpf&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;MsOfficeTracker - Not enough quota available（由于我在此页上有回复，所以此页包含了以上所有链接。）&lt;br /&gt;
  &lt;a href=&quot;https://github.com/sealuzh/PersonalAnalytics/issues/62&quot;&gt;https://github.com/sealuzh/PersonalAnalytics/issues/62&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Pushing the Limits of Windows: Handles&lt;br /&gt;
  &lt;a href=&quot;https://blogs.technet.microsoft.com/markrussinovich/2009/09/29/pushing-the-limits-of-windows-handles/&quot;&gt;https://blogs.technet.microsoft.com/markrussinovich/2009/09/29/pushing-the-limits-of-windows-handles/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Why does my WPF application use up so many Windows handles? [closed]&lt;br /&gt;
  &lt;a href=&quot;https://stackoverflow.com/questions/25316479/why-does-my-wpf-application-use-up-so-many-windows-handles&quot;&gt;https://stackoverflow.com/questions/25316479/why-does-my-wpf-application-use-up-so-many-windows-handles&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;The current process has used all of its system allowance of handles for Window Manager objects&lt;br /&gt;
  &lt;a href=&quot;https://social.msdn.microsoft.com/Forums/windows/en-US/73aaa1f3-30a7-4593-b299-7ec1fd582b27/the-current-process-has-used-all-of-its-system-allowance-of-handles-for-window-manager-objects?forum=winforms&quot;&gt;https://social.msdn.microsoft.com/Forums/windows/en-US/73aaa1f3-30a7-4593-b299-7ec1fd582b27/the-current-process-has-used-all-of-its-system-allowance-of-handles-for-window-manager-objects?forum=winforms&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;0x80070008-存储空间不足无法处理此命令&quot;&gt;0x80070008: 存储空间不足，无法处理此命令。&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;问题状态：尚未解决。&lt;/li&gt;
  &lt;li&gt;复现步骤：复现一次，但难以总结步骤，其它通过日志搜集。&lt;/li&gt;
  &lt;li&gt;表现现象：从唯一的一次复现来看，会导致整个窗口无法操作，无任何响应。&lt;/li&gt;
  &lt;li&gt;问题进展：&lt;br /&gt;
微软说，这是 TextInterface 的内存泄露问题，.NET Framework 4.6 解了，要升 4.6 才行。
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35182703/wpf-error-when-calling-measure-not-enough-storage-is-available-to-process-thi/41341668#41341668&quot;&gt;https://stackoverflow.com/questions/35182703/wpf-error-when-calling-measure-not-enough-storage-is-available-to-process-thi/41341668#41341668&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/350a8d21-f361-4983-9bc3-65c71a78cb52/comexception-this-command-is-not-enough-memory-available?forum=wpf&quot;&gt;https://social.msdn.microsoft.com/Forums/vstudio/en-US/350a8d21-f361-4983-9bc3-65c71a78cb52/comexception-this-command-is-not-enough-memory-available?forum=wpf&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://connect.microsoft.com/VisualStudio/feedback/details/1468770/exception-with-textinterface&quot;&gt;https://connect.microsoft.com/VisualStudio/feedback/details/1468770/exception-with-textinterface&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;msinternaltexttextinterfacenativeutilconverthresulttoexception-里的-systemoutofmemoryexception&quot;&gt;MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException 里的 System.OutOfMemoryException&lt;/h3&gt;

&lt;p&gt;问题状态：跟进中……&lt;/p&gt;

&lt;h2 id=&quot;附异常的详细堆栈&quot;&gt;附：异常的详细堆栈&lt;/h2&gt;

&lt;h4 id=&quot;1-systemcomponentmodelwin32exception-0x80004005-配额不足无法处理此命令&quot;&gt;1. &lt;code class=&quot;highlighter-rouge&quot;&gt;System.ComponentModel.Win32Exception (0x80004005): 配额不足，无法处理此命令。&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.ComponentModel.Win32Exception (0x80004005): Not enough quota is available to process this command.
   at MS.Win32.UnsafeNativeMethods.PostMessage(HandleRef hwnd, WindowMessage msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean enableRenderTarget, Nullable`1 channelSet)
   at System.Windows.Interop.HwndTarget.UpdateWindowPos(IntPtr lParam)
   at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-systemruntimeinteropservicescomexception-0x80070008-存储空间不足无法处理此命令-exception-from-hresult-0x80070008&quot;&gt;2. &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Runtime.InteropServices.COMException (0x80070008): 存储空间不足，无法处理此命令。 (Exception from HRESULT: 0x80070008)&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.Runtime.InteropServices.COMException (0x80070008): Not enough storage is available to process this command. (Exception from HRESULT: 0x80070008)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
   at MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException(Int32 hr)
   at MS.Internal.Text.TextInterface.FontFace.GetDisplayGlyphMetrics(UInt16* pGlyphIndices, UInt32 glyphCount, GlyphMetrics* pGlyphMetrics, Single emSize, Boolean useDisplayNatural, Boolean isSideways, Single pixelsPerDip)
   at System.Windows.Media.GlyphTypeface.GlyphMetrics(UInt16* pGlyphIndices, Int32 characterCount, GlyphMetrics* pGlyphMetrics, Double emSize, Single pixelsPerDip, TextFormattingMode textFormattingMode, Boolean isSideways)
   at System.Windows.Media.GlyphTypeface.GetGlyphMetricsAndIndicesOptimized(UInt32* pCodepoints, Int32 characterCount, Double emSize, Single pixelsPerDip, UInt16[] glyphIndices, GlyphMetrics[] glyphMetrics, TextFormattingMode textFormattingMode, Boolean isSideways)
   at System.Windows.Media.GlyphTypeface.GetGlyphMetricsOptimized(CharacterBufferRange characters, Double emSize, Single pixelsPerDip, UInt16[] glyphIndices, GlyphMetrics[] glyphMetrics, TextFormattingMode textFormattingMode, Boolean isSideways)
   at System.Windows.Media.Typeface.CheckFastPathNominalGlyphs(CharacterBufferRange charBufferRange, Double emSize, Single pixelsPerDip, Double scalingFactor, Double widthMax, Boolean keepAWord, Boolean numberSubstitution, CultureInfo cultureInfo, TextFormattingMode textFormattingMode, Boolean isSideways, Boolean breakOnTabs, Int32&amp;amp; stringLengthFit)
   at MS.Internal.TextFormatting.SimpleRun.CreateSimpleTextRun(CharacterBufferRange charBufferRange, TextRun textRun, TextFormatterImp formatter, Int32 widthLeft, Boolean emergencyWrap, Boolean breakOnTabs, Double pixelsPerDip)
   at MS.Internal.TextFormatting.SimpleRun.Create(FormatSettings settings, CharacterBufferRange charString, TextRun textRun, Int32 cp, Int32 cpFirst, Int32 runLength, Int32 widthLeft, Int32 idealRunOffsetUnRounded, Double pixelsPerDip)
   at MS.Internal.TextFormatting.SimpleTextLine.Create(FormatSettings settings, Int32 cpFirst, Int32 paragraphWidth, Double pixelsPerDip)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLineInternal(TextSource textSource, Int32 firstCharIndex, Int32 lineLength, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLine(TextSource textSource, Int32 firstCharIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at MS.Internal.Text.Line.Format(Int32 dcp, Double width, TextParagraphProperties lineProperties, TextLineBreak textLineBreak, TextRunCache textRunCache, Boolean showParagraphEllipsis)
   at System.Windows.Controls.TextBlock.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Documents.Adorner.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Documents.AdornerLayer.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Window.MeasureOverrideHelper(Size constraint)
   at System.Windows.Window.MeasureOverride(Size availableSize)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
……
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-systemruntimeinteropservicescomexception-0x88980411-exception-from-hresult-0x88980411&quot;&gt;3. &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Runtime.InteropServices.COMException (0x88980411): Exception from HRESULT: 0x88980411&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.Runtime.InteropServices.COMException (0x88980411): Exception from HRESULT: 0x88980411
   at System.Windows.Media.Composition.DUCE.Channel.ReleaseOnChannel(ResourceHandle handle)
   at System.Windows.Media.Composition.DUCE.MultiChannelResource.ReleaseOnChannel(Channel channel)
   at System.Windows.Media.DashStyle.System.Windows.Media.Composition.DUCE.IResource.ReleaseOnChannel(Channel channel)
   at System.Windows.Media.Pen.DashStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.Freezable.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry&amp;amp; newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value)
   at System.Windows.Media.Pen.set_DashStyle(DashStyle value)
   at System.Windows.Shapes.Shape.GetNaturalSize()
   at System.Windows.Shapes.Shape.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.StackPanel.StackMeasureHelper(IStackMeasure measureElement, IStackMeasureScrollData scrollData, Size constraint)
   at System.Windows.Controls.StackPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean&amp;amp; hasDesiredSizeUChanged)
   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at Demo.PopupRoot.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Window.MeasureOverrideHelper(Size constraint)
   at System.Windows.Window.MeasureOverride(Size availableSize)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;4-systemoutofmemoryexception-insufficient-memory-to-continue-the-execution-of-the-program&quot;&gt;4. &lt;code class=&quot;highlighter-rouge&quot;&gt;System.OutOfMemoryException: Insufficient memory to continue the execution of the program.&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.OutOfMemoryException: Insufficient memory to continue the execution of the program. ---&amp;gt; System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
   at MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException(Int32 hr)
   at MS.Internal.Text.TextInterface.TextAnalyzer.GetGlyphs(UInt16* textString, UInt32 textLength, Font font, UInt16 blankGlyphIndex, Boolean isSideways, Boolean isRightToLeft, CultureInfo cultureInfo, DWriteFontFeature[][] features, UInt32[] featureRangeLengths, UInt32 maxGlyphCount, TextFormattingMode textFormattingMode, ItemProps itemProps, UInt16* clusterMap, UInt16* textProps, UInt16* glyphIndices, UInt32* glyphProps, Int32* pfCanGlyphAlone, UInt32&amp;amp; actualGlyphCount)
   at MS.Internal.TextFormatting.LineServicesCallbacks.GetGlyphsRedefined(IntPtr pols, IntPtr* plsplsruns, Int32* pcchPlsrun, Int32 plsrunCount, Char* pwchText, Int32 cchText, LsTFlow textFlow, UInt16* puGlyphsBuffer, UInt32* piGlyphPropsBuffer, Int32 cgiGlyphBuffers, Int32&amp;amp; fIsGlyphBuffersUsed, UInt16* puClusterMap, UInt16* puCharProperties, Int32* pfCanGlyphAlone, Int32&amp;amp; glyphCount)
   --- End of inner exception stack trace ---
   at MS.Internal.TextFormatting.TextMetrics.FullTextLine.FormatLine(FullTextState fullText, Int32 cpFirst, Int32 lineLength, Int32 formatWidth, Int32 finiteFormatWidth, Int32 paragraphWidth, LineFlags lineFlags, FormattedTextSymbols collapsingSymbol)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLineInternal(TextSource textSource, Int32 firstCharIndex, Int32 lineLength, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLine(TextSource textSource, Int32 firstCharIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at MS.Internal.Text.Line.Format(Int32 dcp, Double width, TextParagraphProperties lineProperties, TextLineBreak textLineBreak, TextRunCache textRunCache, Boolean showParagraphEllipsis)
   at System.Windows.Controls.TextBlock.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget)
   at System.Windows.Interop.HwndTarget.OnResize()
   at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 16 Jan 2018 01:57:50 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2017/01/19/there-is-no-code-of-mine-in-the-stack-trace.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2017/01/19/there-is-no-code-of-mine-in-the-stack-trace.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>卡诺模型（KANO Model）</title>
        <description>&lt;p&gt;卡诺模型是一种研究影响顾客满意度因素的方法，在软件工程中可以用来辅助做需求分析和优化产品的质量。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;作为一种方法，卡诺模型将某一种特性的存在程度作为横坐标，越大表示某个功能或特性做得越多，越小则表示做得越少。而纵坐标是用户满意度/认可程度，越高表示用户越喜欢，越低表示用户越讨厌此特性。&lt;/p&gt;

&lt;p&gt;于是，卡诺模型为不同的特性分成五个类别：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2018-01-09-17-12-19.png&quot; alt=&quot;卡诺模型&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;魅力特性&lt;/strong&gt;：如果产品没有此特性，用户并不关心；而产品拥有此特性时，用户会非常满意。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;期望特性&lt;/strong&gt;：如果产品的此特性做得越多，用户会越满意；而做得越少则越不满意。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;必备特性&lt;/strong&gt;：如果此特性做得很好，用户并不会有什么感觉；但没有此特性用户会非常不满意。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;无差异特性&lt;/strong&gt;：此特性不管做多做少，用户都不会在意。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;反向特性&lt;/strong&gt;：如果产品中有此特性，用户会不满意，做得越多越不满意。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;产品的稳定性和性能属于“必备特性”，做到极致时用户感知不到我们对产品稳定性所做的努力，但做不好时却会让体验明显下降。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/22989667&quot;&gt;什么是卡诺KANO模型？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wiki.mbalib.com/wiki/KANO%E6%A8%A1%E5%9E%8B&quot;&gt;KANO模型 - MBA智库百科&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/3c32cd247892&quot;&gt;需求分析神器Kano模型 - 简书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 09 Jan 2018 09:57:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/kano-model.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/kano-model.html</guid>
        
        
        <category>ux</category>
        
      </item>
    
      <item>
        <title>为什么委托的减法（- 或 -=）可能出现非预期的结果？（Delegate Subtraction Has Unpredictable Result）</title>
        <description>&lt;p&gt;当我们为一个委托写 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候，ReSharper 会提示“Delegate Subtraction Has Unpredictable Result”，即“委托的减法可能出现非预期的结果”。然而在写为事件写 &lt;code class=&quot;highlighter-rouge&quot;&gt;-=&lt;/code&gt; 的时候却并没有这样的提示。然而这个提示是什么意思呢？为什么会“非预期”？为什么委托会提示而事件不会提示？&lt;/p&gt;

&lt;p&gt;阅读本文将了解委托的减法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-28-08-51-19.png&quot; alt=&quot;委托的减法提示&quot; /&gt;&lt;br /&gt;
▲ 委托的减法可能出现非预期的结果&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;resharper-的官方帮助文档&quot;&gt;ReSharper 的官方帮助文档&lt;/h2&gt;

&lt;h3 id=&quot;例子和现象&quot;&gt;例子和现象&lt;/h3&gt;

&lt;p&gt;从 ReSharper 的提示中，我们可以跳转到官方帮助文档 &lt;a href=&quot;https://www.jetbrains.com/help/resharper/2017.3/DelegateSubtraction.html&quot;&gt;Code Inspection: Delegate subtractions - Help - ReSharper&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-28-08-54-41.png&quot; alt=&quot;进入 ReSharper 官方帮助文档&quot; /&gt;&lt;/p&gt;

&lt;p&gt;官方文档中给出了一个非常典型的 Demo 程序：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;//ABC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//BC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//AC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//AB&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//C&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//A&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//ABC&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;关键就在最后一行的输出结果。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt; 等于 &lt;code class=&quot;highlighter-rouge&quot;&gt;a + b + c&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;s - (a + c)&lt;/code&gt; 却依然输出 &lt;code class=&quot;highlighter-rouge&quot;&gt;ABC&lt;/code&gt;，而不是前面例子中就像数学加减法一样的输出。&lt;/p&gt;

&lt;p&gt;ReSharper 同时还给出另一个例子，说明委托的减法顺序也可能非预期：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// AB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;它会从尾部减起，而这一点也容易被大家忽视。&lt;/p&gt;

&lt;h3 id=&quot;原理&quot;&gt;原理&lt;/h3&gt;

&lt;p&gt;究其原因，ReSharper 官方文档也已说明。因为委托保存了一个调用列表，委托的 &lt;code class=&quot;highlighter-rouge&quot;&gt;a + b&lt;/code&gt;，是将 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 的调用列表追加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 的调用列表之后；而委托的 &lt;code class=&quot;highlighter-rouge&quot;&gt;a - b&lt;/code&gt; 是从 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; 的调用列表中移除 &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 的调用列表子序列。&lt;/p&gt;

&lt;p&gt;也就是说，&lt;strong&gt;委托的加减其实就是委托调用列表中序列的拼接和子序列的移除&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;用图来表示这个调用列表的加减过程，可以画成这样。其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; 是委托，&lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;z&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;w&lt;/code&gt; 是调用列表中的每一项。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-28-09-41-51.png&quot; alt=&quot;调用列表的加减&quot; /&gt;&lt;br /&gt;
▲ 调用列表的加减其实就是序列的拼接和子序列的移除&lt;/p&gt;

&lt;h2 id=&quot;将委托和事件比较&quot;&gt;将委托和事件比较&lt;/h2&gt;

&lt;p&gt;既然 ReSharper 对委托做出了这样的提示，而事件几乎就是委托的封装，那为何事件不给出提示呢？！&lt;/p&gt;

&lt;p&gt;带着疑问，我将 ReSharper 官方例子中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;s&lt;/code&gt; 改成了事件，其他代码完全一样。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这一句注释掉，因为 s 换成了事件，而事件必须定义在类中。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Action s = a + b + c + Console.WriteLine;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;//ABC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//BC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//AC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)();&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;//AB&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//C&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//A&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;//ABC&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后面用于代表输出结果的注释我依然没改，因为&lt;strong&gt;输出结果真的没变&lt;/strong&gt;！！！也就是说，理论上使用事件并不能帮助减少委托减法带来的结果不确定性。&lt;/p&gt;

&lt;p&gt;但是——事件是观察者模式的一种实现，从设计上说，事件只作通知之用，不确保顺序，也不保证结果。在这个角度上说，如果依然用事件写出上面 demo 那样的“&lt;em&gt;不可预期&lt;/em&gt;”代码，那简直不把事件当事件用。&lt;/p&gt;

&lt;h2 id=&quot;不再用委托减法了吗&quot;&gt;不再用委托减法了吗？&lt;/h2&gt;

&lt;p&gt;至少从设计模式上说，事件里委托减法的的那些非预期就忽略吧，那么没有定义成事件的那些委托呢？我们需要如何处理减法？&lt;/p&gt;

&lt;p&gt;其实，大可不必太担心，因为大多数场合下我们进行委托加法和减法时，都是用一个包含调用列表的委托与其它只有一个调用节点的委托进行加减，通常结果都是符合预期的，也通常不会对顺序敏感。但是，如果委托的减法是库 API 的一部分，那就需要小心，因为库的使用者可能写出任何一种诡异的代码！这种情况下，换成事件是一个不错的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/help/resharper/2017.3/DelegateSubtraction.html&quot;&gt;Code Inspection: Delegate subtractions - Help - ReSharper&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/11180068/delegate-subtraction-has-unpredictable-result-in-resharper-c&quot;&gt;events - “Delegate subtraction has unpredictable result” in ReSharper/C#? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 28 Dec 2017 02:03:44 +0000</pubDate>
        <link>https://blog.walterlv.com/post/delegate-subtraction-has-unpredictable-result.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/delegate-subtraction-has-unpredictable-result.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>使 32 位程序使用大于 2GB 的内存</title>
        <description>&lt;p&gt;不管在 32 位 Windows 上还是在 64 位 Windows 上，32 位的应用程序都只能使用最大 2GB 的内存，这是我们司空见惯的一个设定。但其实 Windows 提供了一些方法让我们打破这样的设定，使程序使用大于 2GB 的内存。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;为什么-32-位程序只能使用最大-2gb-内存&quot;&gt;为什么 32 位程序只能使用最大 2GB 内存？&lt;/h2&gt;

&lt;p&gt;32 位寻址空间只有 4GB 大小，于是 32 位应用程序进程最大只能用到 4GB 的内存。然而，除了应用程序本身要用内存，操作系统内核也需要使用。应用程序使用的内存空间分为用户空间和内核空间，每个 32 位程序的用户空间可独享前 2GB 空间（指针值为正数），而内核空间为所有进程共享 2GB 空间（指针值为负数）。所以，32 位应用程序实际能够访问的内存地址空间最多只有 2GB。&lt;/p&gt;

&lt;h2 id=&quot;让-32-位程序使用大于-2gb-内存的两种方法&quot;&gt;让 32 位程序使用大于 2GB 内存的两种方法&lt;/h2&gt;

&lt;h3 id=&quot;editbin&quot;&gt;editbin&lt;/h3&gt;

&lt;p&gt;这是 Visual Studio 2017 采用的做法。我们需要使用到两个工具——&lt;code class=&quot;highlighter-rouge&quot;&gt;editbin&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;dumpbin&lt;/code&gt;。前者用于编辑我们编译生成好的程序使之头信息中声明支持大于 2GB 内存，后者用于查看程序的头信息验证我们是否改好了。&lt;/p&gt;

&lt;p&gt;编辑一个程序使之声明支持大于 2GB 内存的命令是：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;editbin /largeaddressaware xxx.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;xxx.exe&lt;/code&gt; 是我们准备修改的程序，可以使用相对路径或绝对路径（如果路径中出现空格记得带引号）。&lt;/p&gt;

&lt;p&gt;验证这个程序是否改好了的命令是：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dumpbin /headers xxx.exe | more
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样，&lt;code class=&quot;highlighter-rouge&quot;&gt;xxx.exe&lt;/code&gt; 是我们刚刚改好准备检查的程序，可以使用相对路径或绝对路径。&lt;/p&gt;

&lt;p&gt;editbin 改之前和改之后用 dumpbin 查看我们的程序头信息，得到下面两张图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-12-normal-32bit-header.png&quot; alt=&quot;改之前&quot; /&gt;
&lt;img src=&quot;/static/posts/2017-09-12-large-address-32bit-header.png&quot; alt=&quot;改之后&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;FILE HEADER VALUES&lt;/code&gt; 块的倒数第二行多出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Application can handle large (&amp;gt;2GB) addresses&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果没发现，一定是你命令执行中发生了错误，检查一下吧！最容易出现的错误是执行后发现&lt;strong&gt;根本就没有这个命令&lt;/strong&gt;。是的，&lt;code class=&quot;highlighter-rouge&quot;&gt;editbin&lt;/code&gt; 命令从哪里来呢？可以在开始菜单中的 Visual Studio 文件夹中查找 Developer Command Prompt for VS 2017，运行这个启动的命令行中就带有 editbin 和 dumpbin。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-12-where-to-find-editbin.png&quot; alt=&quot;本机工具提示符&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果希望能够在 Visual Studio 编译的时候自动调用这个工具，请参见：&lt;a href=&quot;https://stackoverflow.com/questions/31565532/largeaddressaware-visual-studio-2015-c-sharp&quot;&gt;LargeAddressAware Visual Studio 2015 C#&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;编译成-anycpu-prefer-32-bit&quot;&gt;编译成 AnyCPU (Prefer 32-bit)&lt;/h3&gt;

&lt;p&gt;这是本文更推荐的做法，也是最简单的做法。方法是打开入口程序集的属性页，将“目标平台”选为“AnyCPU”，然后勾选“首选 32 位”。需要注意的是，这种生成方式是 .NET Framework 4.5 及以上版本才提供的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-12-anycpu-with-32bit-preferred-build.png&quot; alt=&quot;AnyCPU (Prefer 32-bit)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;至于 AnyCPU (Prefer 32-bit) 和 x86 两种生成方式的区别，请参见：&lt;a href=&quot;https://lindexi.github.io/lindexi/post/WPF-%E7%BC%96%E8%AF%91%E4%B8%BA-AnyCPU-%E5%92%8C-x86-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB.html&quot;&gt;WPF 编译为 AnyCPU 和 x86 有什么区别 - 林德熙&lt;/a&gt; 和 &lt;a href=&quot;https://stackoverflow.com/questions/12066638/what-is-the-purpose-of-the-prefer-32-bit-setting-in-visual-studio-2012-and-how&quot;&gt;What is the purpose of the “Prefer 32-bit” setting in Visual Studio 2012 and how does it actually work?&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;声明支持大于-2gb-内存后能使用多少内存&quot;&gt;声明支持大于 2GB 内存后，能使用多少内存？&lt;/h2&gt;

&lt;p&gt;对于 32 位操作系统，程序依然只能使用 2GB 内存，除非开启了 &lt;code class=&quot;highlighter-rouge&quot;&gt;/3GB&lt;/code&gt; 开关，开启方法详见：&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/hardware/ff556232(v=vs.85).aspx&quot;&gt;/3GB&lt;/a&gt;。开启后，应用程序的用户态将可以使用 3GB 内存，但内核态将只能使用 1GB 内存。微软认为，是否打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;/3GB&lt;/code&gt; 开关是计算机设备开发商需要做的事情，开发商也需要自己测试开启后驱动程序的性能表现和稳定性。&lt;/p&gt;

&lt;p&gt;对于 64 位操作系统，Windows 将很豪放地将 4GB 全部贡献给这样的程序，因为系统自己已经有更多的内存寻址空间可以使用了，没必要跟 32 位应用程序抢占寻址空间。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;AnyCPU (32bit preferred)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/12066638/what-is-the-purpose-of-the-prefer-32-bit-setting-in-visual-studio-2012-and-how&quot;&gt;What is the purpose of the “Prefer 32-bit” setting in Visual Studio 2012 and how does it actually work?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E7%BC%96%E8%AF%91%E4%B8%BA-AnyCPU-%E5%92%8C-x86-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB.html&quot;&gt;WPF 编译为 AnyCPU 和 x86 有什么区别 - 林德熙&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;IMAGE_FILE_LARGE_ADDRESS_AWARE
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx&quot;&gt;Memory Limits for Windows and Windows Server Releases&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://superuser.com/questions/176869/getting-32-bit-application-to-use-more-than-2gb-on-64-bit-windows-7&quot;&gt;Getting 32-bit application to use more than 2GB on 64-bit Windows 7?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/wz223b1z.aspx&quot;&gt;/LARGEADDRESSAWARE (Handle Large Addresses)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/2740308/why-2-gb-memory-limit-when-running-in-64-bit-windows&quot;&gt;Why 2 GB memory limit when running in 64 bit Windows?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://blogs.technet.microsoft.com/markrussinovich/2009/03/10/pushing-the-limits-of-windows-paged-and-nonpaged-pool/&quot;&gt;Pushing the Limits of Windows: Paged and Nonpaged Pool&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/570589/can-a-32bit-process-access-more-memory-on-a-64bit-windows-os&quot;&gt;Can a 32bit process access more memory on a 64bit windows OS?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;/3GB
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/hardware/ff556232(v=vs.85).aspx&quot;&gt;/3GB&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;editbin/dumpbin
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;editbin /largeaddressaware xxx.exe&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dumpbin /headers xxx.exe | more&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3979624/verify-if-largeaddressaware-is-in-effect&quot;&gt;verify if largeAddressAware is in effect?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/31565532/largeaddressaware-visual-studio-2015-c-sharp&quot;&gt;LargeAddressAware Visual Studio 2015 C#&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 26 Dec 2017 01:05:27 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2017/09/12/32bit-application-use-large-memory.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2017/09/12/32bit-application-use-large-memory.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>让 ScrollViewer 的滚动带上动画</title>
        <description>&lt;p&gt;WPF 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollViewer&lt;/code&gt; 没有水平滚动和垂直滚动的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;HorizontalScrollOffset&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;VerticalScrollOffset&lt;/code&gt;，只有水平滚动和垂直滚动的方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollToHorizontalOffset&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollToVerticalOffset&lt;/code&gt;，那么怎么给滚动过程加上动画呢？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;既然没有属性，那我们加个属性好了，反正附加属性就是用来干这个事儿的。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ScrollViewerBehavior&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HorizontalOffsetProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HorizontalOffset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScrollViewerBehavior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UIPropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnHorizontalOffsetChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetHorizontalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HorizontalOffsetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHorizontalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HorizontalOffsetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnHorizontalOffsetChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScrollViewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScrollToHorizontalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VerticalOffsetProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterAttached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VerticalOffset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScrollViewerBehavior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UIPropertyMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnVerticalOffsetChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetVerticalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VerticalOffsetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetVerticalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FrameworkElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VerticalOffsetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnVerticalOffsetChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DependencyPropertyChangedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScrollViewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScrollToVerticalOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在属性的变更通知中调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollToHorizontalOffset&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollToVerticalOffset&lt;/code&gt; 方法。这样，便能够通过动画改变属性的方式来调用这两个方法。&lt;/p&gt;

&lt;p&gt;那么现在我们就加上动画：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScrollStoryboard&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScrollViewer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(walterlv:ScrollViewerBehavior.HorizontalOffset)&quot;&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;From=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;500&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:0.6&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation.EasingFunction&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;CircleEase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EaseOut&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DoubleAnimation.EasingFunction&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/DoubleAnimation&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;添加一些用于测试的按钮和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollViewer&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ScrollViewer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.RowSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ScrollViewer&quot;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;HorizontalScrollBarVisibility=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visible&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalScrollBarVisibility=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visible&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Image&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://walterlv.github.io/static/posts/2017-12-09-21-19-50.png&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ScrollViewer&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ConnectionDestination&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bottom&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;动画目标&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Panel.ZIndex=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Button.Triggers&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;EventTrigger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RoutedEvent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Button.Click&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;BeginStoryboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource ScrollStoryboard}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/EventTrigger&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button.Triggers&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在，我们点击按钮，就可以看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollViewer&lt;/code&gt; 的滚动动画生效了，如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-19-scroll-viewer-animation.gif&quot; alt=&quot;动画效果&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;额外的，如果希望这个附加属性能够附加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListView&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListBox&lt;/code&gt; 中，则需要修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollViewerBehavior&lt;/code&gt; 类，然后在 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnHorizontalOffsetChanged&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;OnVerticalOffsetChanged&lt;/code&gt; 方法中判断 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListView&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListBox&lt;/code&gt;，然后在其中寻找可视元素子级 &lt;code class=&quot;highlighter-rouge&quot;&gt;ScrollViewer&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Dec 2017 12:19:41 +0000</pubDate>
        <link>https://blog.walterlv.com/post/scrollviewer-animation.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/scrollviewer-animation.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>真的要比较 for 和 foreach 的性能吗？（内附性能比较的实测数据）</title>
        <description>&lt;p&gt;小伙伴告诉我，&lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;T&amp;gt;.Find&lt;/code&gt; 方法比 &lt;code class=&quot;highlighter-rouge&quot;&gt;List.FirstOrDefault&lt;/code&gt; 扩展方法性能更高，详见：&lt;a href=&quot;https://blog.lindexi.com/post/C-Find-vs-FirstOrDefault.html&quot;&gt;C＃ Find vs FirstOrDefault - 林德熙&lt;/a&gt;。这可让我震惊了，因为我从来都没有考虑过在如此微观尺度衡量它们的性能差异。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;少不了的源码&quot;&gt;少不了的源码&lt;/h2&gt;

&lt;p&gt;于是，我立刻翻开了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 的源代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ThrowHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionArgument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArgumentNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArgumentNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;predicate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这难道不是在 PK for 和 foreach 吗？接下来的分析才发现，没这么简单。&lt;/p&gt;

&lt;h2 id=&quot;find-vs-firstordefault&quot;&gt;Find V.S. FirstOrDefault&lt;/h2&gt;

&lt;p&gt;我写了两段代码，然后在单元测试中测量它们的性能。方法我按不同顺序写了两遍，试图降低初始化影响和偶然事件的影响。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FindAndFirstOrDefaultTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindAndFirstOrDefaultTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A0_Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A1_FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B0_FirstOrDefault2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B1_Find2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;100 长度的 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;int&amp;gt;&lt;/code&gt;，其性能数据如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-21-38-53.png&quot; alt=&quot;100 长度的 List&quot; /&gt;&lt;/p&gt;

&lt;p&gt;很明显，数据量太少不好测量，也收到单元测试本身的影响。我们需要增大数据量，以减少那些因素的影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-21-38-07.png&quot; alt=&quot;10000000 长度的 List&quot; /&gt;&lt;/p&gt;

&lt;p&gt;居然真的存在性能差异！！！而且，&lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 性能的两倍！！！&lt;/p&gt;

&lt;p&gt;这似乎能够解释，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 毕竟还要生成 &lt;code class=&quot;highlighter-rouge&quot;&gt;IEnumerator&lt;/code&gt; 对象，还要有方法调用；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 却只有 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; 集合的访问。然而，这真的只是 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 之间的性能差异吗？&lt;/p&gt;

&lt;h2 id=&quot;for-vs-foreach&quot;&gt;for V.S. foreach&lt;/h2&gt;

&lt;p&gt;为了看看其性能差异来自于 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt;，我把 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 的调用修改为 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ForAndForeachTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ForAndForeachTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A0_Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A1_FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B0_FirstOrDefault2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B1_Find2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一样，100 长度的 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;int&amp;gt;&lt;/code&gt; 并没有参考性：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-21-29-20.png&quot; alt=&quot;100 长度的 List&quot; /&gt;&lt;/p&gt;

&lt;p&gt;50000000 长度的则可以减少影响：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-21-35-30.png&quot; alt=&quot;50000000 长度的 List&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而结论居然是——&lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 比 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 有“&lt;strong&gt;轻微&lt;/strong&gt;”的性能优势！这与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 两倍的性能差异就小多了。是什么原因造成了如此的性能差异呢？&lt;/p&gt;

&lt;h2 id=&quot;轻微的性能优势还是两倍的性能优势&quot;&gt;轻微的性能优势，还是两倍的性能优势？&lt;/h2&gt;

&lt;p&gt;为了了解原因，我将 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 中的方法写到测试里面：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了能够不让数据超过 1 秒导致单元测试计时精度降低，我将长度减小到了 40000000。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-22-02-31.png&quot; alt=&quot;40000000 长度的 List&quot; /&gt;&lt;br /&gt;
▲ 调用 For 和 Foreach&lt;/p&gt;

&lt;p&gt;性能相比于直接写 &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;foreach&lt;/code&gt; 有轻微的损失，但是调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;For&lt;/code&gt; 和调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foreach&lt;/code&gt; 却并没有两倍的性能差异，虽然方法的实现与 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 几乎一模一样！&lt;/p&gt;

&lt;p&gt;而且，相同数量的 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;int&amp;gt;&lt;/code&gt;，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 居然比自己写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;For&lt;/code&gt; 更快，调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; 却比自己写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Foreach&lt;/code&gt; 更慢：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-22-12-12.png&quot; alt=&quot;40000000 长度的 List&quot; /&gt;&lt;br /&gt;
▲ 调用 Find 和 FirstOrDefault&lt;/p&gt;

&lt;p&gt;我写的 &lt;code class=&quot;highlighter-rouge&quot;&gt;For&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 中一定还存在着哪里不一样——对，是索引器！&lt;/p&gt;

&lt;p&gt;以下是 &lt;code class=&quot;highlighter-rouge&quot;&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; 索引器的源码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Following trick can reduce the range check by one&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ThrowHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowArgumentOutOfRangeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ThrowHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowArgumentOutOfRangeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndContractBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;For&lt;/code&gt; 内部索引访问相比于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt; 内部索引访问多了数组越界判断，同时还可能存在 JIT 的特别优化。如果要验证这个问题，我就需要比较数组了。&lt;/p&gt;

&lt;h2 id=&quot;list-vs-array&quot;&gt;List V.S. Array&lt;/h2&gt;

&lt;p&gt;改写我们的测试代码，这回的 &lt;code class=&quot;highlighter-rouge&quot;&gt;For&lt;/code&gt; 方法有两个重载，一个列表一个数组。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A0_List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_A1_Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_testArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B0_Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_testArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_B1_List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_testTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样的数据量：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-12-07-23-01-55.png&quot; alt=&quot;列表和数组&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以发现，即便是数组，其性能也赶不上原生的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Find&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;只有现象却没有结论&quot;&gt;只有现象，却没有结论&lt;/h2&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.lindexi.com/post/C-Find-vs-FirstOrDefault.html&quot;&gt;C＃ Find vs FirstOrDefault - 林德熙&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/365615/in-net-which-loop-runs-faster-for-or-foreach&quot;&gt;c# - In .NET, which loop runs faster, ‘for’ or ‘foreach’? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://codebetter.com/patricksmacchia/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances/&quot;&gt;An easy and efficient way to improve .NET code performances - Patrick Smacchia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.dotnetperls.com/for-foreach&quot;&gt;C# For Versus Foreach Performance - Dot Net Perls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 07 Dec 2017 15:30:35 +0000</pubDate>
        <link>https://blog.walterlv.com/post/for-vs-foreach.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/for-vs-foreach.html</guid>
        
        
        <category>csharp</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>当我们使用 MVVM 模式时，我们究竟在每一层里做些什么？</title>
        <description>&lt;p&gt;这篇文章不会说 MVVM 是什么，因为讲这个的文章太多了；也不会说 MVVM 的好处，因为这样的文章也是一搜一大把。我只是想说说我们究竟应该如何理解 M-V-VM，当我们真正开始写代码时，应该在里面的每一层里写些什么。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;MVVM，当然三层——M-V-VM。就凭这个“三层”结构，WPF/UWP 开发者们就能折腾出一个完整的程序出来。M——定义数据模型啊，V——视图啊，VM——视图模型。其中 M 和 V 的中文词语和英文单词是很好理解的，但是 VM 就不是个日常用词；于是各种不知道应该放在哪里的代码便一窝蜂全放进了 VM 中，最终导致了 VM 的无限膨胀，成百上千行也是司空见惯啊！&lt;/p&gt;

&lt;p&gt;可是，若 VM 不膨胀，那让 M 或者 V 膨胀吗？当然不是，谁都不要膨胀！于是那么多的代码写到哪里呢？&lt;/p&gt;

&lt;p&gt;答案：&lt;strong&gt;MVVM 之外&lt;/strong&gt;。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;我们的代码不止-mvvm-三层&quot;&gt;我们的代码不止 MVVM 三层&lt;/h2&gt;

&lt;p&gt;MVVM 不是应用程序架构，只是一个 GUI 类程序的开发模式而已。这意味着它只是用来解决我们应用程序中 GUI 部分的开发问题，并不能用来解决其他问题。而一个能持续发展的程序怎么能只有 GUI 呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;MVVM 只是数据驱动型 GUI 程序建议的开发模式；无论是三层中的哪一层，本质上都是在解决 UI 问题。&lt;/strong&gt;&lt;br /&gt;
而非 UI 问题根本就不在 MVVM 的讨论之列。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不知看到这里时你会不会喷我一脸——“V”解决 UI 问题也就算了，“VM”和“M”算什么 UI！&lt;/p&gt;

&lt;p&gt;VM，视图模型。其本质是模型。什么的模型？“视图”的模型。这是为真实的 UI 做的一层抽象模型。也就是说，VM 其实是“抽象的 UI”。&lt;/p&gt;

&lt;p&gt;接着喷——“V”和“VM”解决 UI 问题也就算了，“M”算什么 UI！&lt;/p&gt;

&lt;p&gt;M，数据模型。作为数据驱动型 GUI 程序，这些数据是用于驱动 UI 的数据；比如网络请求的数据，本地文件储存的数据。定义这些数据模型是为了与其他组件、其他程序、其他设备传递数据，并将这些数据为视图模型所用。那些不驱动 UI 的数据根本不在此谈论之列。如果你觉得这样的解释有些牵强，那我也无话可说；但是当我们将它理解成“驱动 UI 的数据”时，我们将能够更容易地组织我们的代码，使之不容易发生混乱。&lt;/p&gt;

&lt;p&gt;MVVM 模式按此理解后，我们将更能够将代码放到合适的位置，避免 VM 代码的膨胀：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;公共的控件或者辅助代码应该抽出来放到别处，比如形成公共组件&lt;/li&gt;
  &lt;li&gt;一些非 UI 的业务功能单独做，独立于 MVVM 模式，对 VM 提供调用接口即可。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;mvvm应该做什么不应该做什么&quot;&gt;MVVM，应该做什么，不应该做什么&lt;/h2&gt;

&lt;p&gt;这一节内容部分参考自：&lt;a href=&quot;http://w3cgeek.com/mvvm-standardization.html&quot;&gt;MVVM standardization - W3Cgeek&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;view&quot;&gt;View&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;想进行测试的逻辑都不要放到这里&lt;/li&gt;
    &lt;li&gt;不止能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Page&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;UserControl&lt;/code&gt;，还能是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Control&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;可以考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTrigger&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ValueConverter&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualState&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;Blend&lt;/code&gt; 中提供的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Behivor&lt;/code&gt; 机制来处理 ViewModel 对应的 UI 展现方式&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;viewmodel&quot;&gt;ViewModel&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;这里需要保持抽象 UI 的状态，这样才可以在据此 ViewModel 创建多个 View 的时候，这些 View 能够完全一致而不用把此前逻辑再跑一边&lt;/li&gt;
    &lt;li&gt;无论如何都不能引用 View，就算是接口也不行&lt;/li&gt;
    &lt;li&gt;注意不要去调用一些单例类或者带状态的静态类，这样才好进行单元测试&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;model&quot;&gt;Model&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;那些通过各种途径搜罗来的数据&lt;/li&gt;
    &lt;li&gt;不能引用 View，也不能引用 ViewModel&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;view-通知-viewmodel&quot;&gt;View 通知 ViewModel&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;推荐用数据绑定&lt;/li&gt;
    &lt;li&gt;尽量不要直接调用 ViewModel，但必要的时候也可以去调用&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;viewmodel-通知-view&quot;&gt;ViewModel 通知 View&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;属性绑定&lt;/li&gt;
    &lt;li&gt;事件通知&lt;/li&gt;
    &lt;li&gt;消息（比如 EventAggregator/Message/RX 框架）&lt;/li&gt;
    &lt;li&gt;通过中间服务调用&lt;/li&gt;
    &lt;li&gt;直接由 View 传入一个委托，ViewModel 去调用那个委托&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.rsuter.com/recommendations-best-practices-implementing-mvvm-xaml-net-applications/&quot;&gt;Recommendations and best practices for implementing MVVM and XAML/.NET applications « Rico Suter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://w3cgeek.com/mvvm-standardization.html&quot;&gt;MVVM standardization - W3Cgeek&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 29 Nov 2017 17:29:14 +0000</pubDate>
        <link>https://blog.walterlv.com/post/mvvm-do-and-dont.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/mvvm-do-and-dont.html</guid>
        
        
        <category>windows</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>极限压缩 PNG</title>
        <description>&lt;p&gt;为了让博客的访问者有更快的访问速度，同时兼顾显示效果，我们有些选择却不多——比如选用 WebP 格式。但考虑到浏览器兼容性问题，有时不得不考虑依然 PNG。&lt;/p&gt;

&lt;p&gt;这里我找到一款极限 PNG 压缩工具——LimitPNG。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;http://nullice.com/limitPNG/&quot;&gt;limitPNG - PNG 图片极限压缩工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://nullice.com/limitPNG/img/gif3.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是 &lt;a href=&quot;http://nullice.com/&quot;&gt;nullice · 不知语冰&lt;/a&gt; 的软件。在极限压缩的时候，压缩一张 PNG 的耗时真的很长，几分钟算是很理想的状态了。部分图片压缩比依然不够大，不过如果愿意丢失一点点精度，可以换取非常大的压缩比提升。&lt;/p&gt;

&lt;p&gt;考虑到大量图片批量压缩，作者又做了另外一款软件：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://nullice.com/archives/1998&quot;&gt;gluttonyPNG – 大批量 PNG 图片压缩工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;于是，应该能应付日常各种需要极限压缩的场景了。&lt;/p&gt;
</description>
        <pubDate>Wed, 29 Nov 2017 12:17:21 +0000</pubDate>
        <link>https://blog.walterlv.com/post/limit-png.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/limit-png.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>自定义 Windows PowerShell 和 cmd 的字体</title>
        <description>&lt;p&gt;Windows 系统下的命令行界面，字体要么是点阵字体，要么是宋体；但无论哪种，始终觉得难看了。然而，字体选择界面却始终没办法选择到我们新安装的各种字体。&lt;/p&gt;

&lt;p&gt;本文将推荐一款可以为 PowerShell 和 cmd 使用的等宽字体，适合程序员使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;对字体要求&quot;&gt;对字体要求&lt;/h2&gt;

&lt;p&gt;当然，安装了 git 后，会自动帮我们安装 mintty，bash 风格，自定义方便，着色也很棒。如果可能，我还是更希望用 mintty。可是，总有免不了要用 cmd 的时候，或者虽然强大但很丑的 PowerShell……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-00-01-33.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 很丑的 cmd&lt;/p&gt;

&lt;p&gt;微软说，cmd 和 PowerShell 对字体的要求非常苛刻，在 &lt;a href=&quot;https://support.microsoft.com/zh-cn/help/247815/necessary-criteria-for-fonts-to-be-available-in-a-command-window&quot;&gt;Necessary criteria for fonts to be available in a command window&lt;/a&gt; 一文种就有说到：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The fonts must meet the following criteria to be available in a command session window:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;The font must be a fixed-pitch font.&lt;/li&gt;
    &lt;li&gt;The font cannot be an italic font.&lt;/li&gt;
    &lt;li&gt;The font cannot have a negative A or C space.&lt;/li&gt;
    &lt;li&gt;If it is a TrueType font, it must be FF_MODERN.&lt;/li&gt;
    &lt;li&gt;If it is not a TrueType font, it must be OEM_CHARSET.
Additional criteria for Asian installations:&lt;/li&gt;
    &lt;li&gt;If it is not a TrueType font, the face name must be “Terminal.”&lt;/li&gt;
    &lt;li&gt;If it is an Asian TrueType font, it must also be an Asian character set.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;翻译过来是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;要能在命令行种使用，字体必须满足：&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;必须是等宽字体&lt;/li&gt;
    &lt;li&gt;不能是斜体&lt;/li&gt;
    &lt;li&gt;该字体不能有A或C负空间&lt;/li&gt;
    &lt;li&gt;如果是 TrueType 字体，则它必须是 FF_MODERN&lt;/li&gt;
    &lt;li&gt;如果不是 TrueType 字体，则它必须是 OEM_CHARSET
如果是给亚洲地区使用，还必须满足这些条件：&lt;/li&gt;
    &lt;li&gt;如果不是 TrueType 字体，字体名必须是“Terminal”&lt;/li&gt;
    &lt;li&gt;如果是亚洲的 TrueType 字体，还必须使用亚洲的字符集。&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;这还真不是一般字体能够满足的……&lt;/p&gt;

&lt;h2 id=&quot;推荐可用的字体&quot;&gt;推荐可用的字体&lt;/h2&gt;

&lt;p&gt;我找了好几款字体，然而只发现下面两款字体是真正可以在 PowerShell 或 cmd 里面用的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://be5invis.github.io/Iosevka/inziu.html&quot;&gt;Inziu Iosevka&lt;/a&gt;&lt;br /&gt;
作者：&lt;a href=&quot;https://www.zhihu.com/people/be5invis&quot;&gt;Belleve - 微软字体设计师，新中文字体主催&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Microsoft/BashOnWindows/files/1362006/Microsoft.YaHei.Mono.zip&quot;&gt;Microsoft YaHei Mono&lt;/a&gt; on GitHub&lt;br /&gt;
微软为 WSL/Bash on Ubuntu on Windows 设计的字体，PowerShell 和 cmd 也能用&lt;br /&gt;
效果相当于微软雅黑和 Consolas 的混搭&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而发现能用的都出自微软之手……&lt;/p&gt;

&lt;p&gt;Inziu 字体族较多，实测有些有效有些无效：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-00-16-27.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以，我更倾向于推荐 &lt;a href=&quot;https://github.com/Microsoft/BashOnWindows/files/1362006/Microsoft.YaHei.Mono.zip&quot;&gt;Microsoft YaHei Mono&lt;/a&gt;，效果如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-00-24-33.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ PowerShell&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-22-23-14-57.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ cmd&lt;/p&gt;

&lt;h2 id=&quot;控制台字体设置方法&quot;&gt;控制台字体设置方法&lt;/h2&gt;

&lt;p&gt;对于上面推荐的两款字体，直接安装就可以了，下次打开 PowerShell 或者 cmd 时，属性界面里面就可以找到新安装的字体，就可以选择了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-00-20-34.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 属性&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-23-00-21-14.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 选择字体&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/zh-cn/help/247815/necessary-criteria-for-fonts-to-be-available-in-a-command-window&quot;&gt;Necessary criteria for fonts to be available in a command window&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/36344262&quot;&gt;为什么 Windows 下 cmd 和 PowerShell 不能方便地自定义字体？ - 知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/20541456/list-of-all-colors-available-for-powershell&quot;&gt;List of all colors available for powershell? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 22 Nov 2017 16:26:13 +0000</pubDate>
        <link>https://blog.walterlv.com/post/customize-fonts-of-command-window.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/customize-fonts-of-command-window.html</guid>
        
        
        <category>windows</category>
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>从 Matrix 解构出 Translate/Scale/Rotate（平移/缩放/旋转）</title>
        <description>&lt;p&gt;在 XAML 中，我们对一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIElement&lt;/code&gt; 进行一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransform&lt;/code&gt; 是再常见不过的事情了，我们可以从众多叠加的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TransformGroup&lt;/code&gt; 瞬间得到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix&lt;/code&gt; 表示整个变换的综合变换矩阵，然而反过来却不好做——从变换矩阵中反向得到变换分量。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;首先明确的是，各种 &lt;code class=&quot;highlighter-rouge&quot;&gt;TranslateTransform&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;RotateTransform&lt;/code&gt; 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix&lt;/code&gt; 具有唯一确定的解，然而反向转换却是有无穷多个解的。于是如果我们要得到一个解，我们需要给定一个条件，然后得到这个条件下的其中一个解。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;为了写出一个通用的变换方法来，我准备了一个测试控件，并为它随意填写一个变换：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisplayShape&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#FF1B6CB0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;UIElement.RenderTransform&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TransformGroup&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ScaleTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleX=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.8&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ScaleY=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SkewTransform/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;RotateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Angle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-48.366&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TranslateTransform&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;X=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;85&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Y=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;160&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/TransformGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/UIElement.RenderTransform&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{media:LuminanceForeground TargetName=DisplayShape}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;HorizontalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;▲ &lt;code class=&quot;highlighter-rouge&quot;&gt;LuminanceForeground&lt;/code&gt; 的作用可参见我的另一篇文章：&lt;a href=&quot;/post/get-gray-reversed-color&quot;&gt;计算能在任何背景色上清晰显示的前景色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-20-23-47-06.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 一个随便应用了一个变换的控件&lt;/p&gt;

&lt;p&gt;我们将从这个控件中取得变换矩阵 &lt;code class=&quot;highlighter-rouge&quot;&gt;Matrix&lt;/code&gt;，然后计算出变换分量的一个解，应用到新的控件上：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TraceShape&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stroke=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#FFE2620A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;StrokeThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-20-23-50-08.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 我们希望计算一组变换分量以便让这个框追踪变换了的控件&lt;/p&gt;

&lt;p&gt;于是，我们写下了测试代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExtractMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScaleTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScaleX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScaleY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RotateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Angle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExtractMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 我们希望在这里写出一个方法，以便得到三个变换分量。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;OnLoaded&lt;/code&gt; 是为了让代码运行起来，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExtractMatrix&lt;/code&gt; 才是我们的核心——将变换分量解构出来。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;思路和初步成果&quot;&gt;思路和初步成果&lt;/h2&gt;

&lt;p&gt;我们的思路是创造一个单位矩形，让它应用这个变换，然后测量变换后矩形的宽高变化，角度变化和位置变化。由于直接使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Rect&lt;/code&gt; 类型时无法表示旋转后的矩形，所以我们直接使用四个顶点来计算，于是我们写出如下代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExtractMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AngleBetween&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行后，我们发现追踪框已经与原始控件完全贴合，说明计算正确。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-20-23-57-54.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 追踪框完全贴合&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;可以灵活应用计算结果&quot;&gt;可以灵活应用计算结果&lt;/h2&gt;

&lt;p&gt;不过如果真要在产品中做追踪框，肯定不能像上图那样被严重拉伸。所以，我们把缩放分量去掉，换成尺寸变化：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExtractMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RotateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Angle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码中，&lt;code class=&quot;highlighter-rouge&quot;&gt;ScaleTransform&lt;/code&gt; 已经被去掉，取而代之的是宽高的设置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-21-00-01-43.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 没有被拉伸的追踪框&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;更通用的方法&quot;&gt;更通用的方法&lt;/h2&gt;

&lt;p&gt;以上虽然达到了目的，不过实际应用中可能会有更多的限制，例如：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;变换中心不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;(0, 0)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;最终应用的顺序不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Scale&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Rotate&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Translate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先来解决变换中心的通用性问题。&lt;/p&gt;

&lt;p&gt;我们将变换中心设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;(0.5, 0.5)&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Rectangle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TraceShape&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Stroke=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#FFE2620A&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;StrokeThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RenderTransformOrigin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.5 0.5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是，追踪框不知道飞到哪里去了……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-21-00-14-25.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
▲ 改变了变换中心&lt;/p&gt;

&lt;p&gt;这时，我们需要将变换中心导致的额外平移量考虑在内。&lt;/p&gt;

&lt;p&gt;如果 S 表示所求变换的缩放分量，R 表示所求变换的旋转分量，T 表示所求变换的平移分量；M 表示需要模拟的目标矩阵。那么，S 将可以通过缩放比和参数指定的缩放中心唯一确定；R 将可以通过旋转角度和参数指定的旋转中心唯一确定；T 不能确定，是我们要求的。&lt;/p&gt;

&lt;p&gt;由于我们按照缩放-&amp;gt;旋转-&amp;gt;平移的顺序模拟 M，所以：&lt;/p&gt;

\[SRT=M\]

&lt;p&gt;即：&lt;/p&gt;

\[T=S^{-1}R^{-1}M\]

&lt;p&gt;所以，我们在上面的之前成果的代码上再做些额外的处理，加上以上公式的推导结果：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MatrixToGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CenterSpecification&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;specifyCenter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 生成一个单位矩形（0, 0, 1, 1），计算单位矩形经矩阵变换后形成的带旋转的矩形。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 于是，我们将可以通过比较这两个矩形中点的数据来求出一个解。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 测试单位矩形宽高的长度变化量，以求出缩放比（作为参数 specifyCenter 中变换中心的计算参考）。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 测试单位向量的旋转变化量，以求出旋转角度。&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AngleBetween&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformedPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 如果指定了变换分量的变换中心点。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;specifyCenter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 那么，就获取指定的变换中心点（缩放中心和旋转中心）。&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scalingCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotationCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;specifyCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 如果 S 表示所求变换的缩放分量，R 表示所求变换的旋转分量，T 表示所求变换的平移分量；M 表示传入的目标矩阵。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 那么，S 将可以通过缩放比和参数指定的缩放中心唯一确定；R 将可以通过旋转角度和参数指定的旋转中心唯一确定。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// S = scaleMatrix; R = rotateMatrix.&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaleMatrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scaleMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScaleAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateMatrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rotateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RotateAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotationCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotationCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// T 是不确定的，它会受到 S 和 T 的影响；但确定等式 SRT=M，即 T=S^{-1}R^{-1}M。&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// T = translateMatrix; M = matrix.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scaleMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rotateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translateMatrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Multiply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaleMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;translateMatrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Multiply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 用考虑了变换中心的平移量覆盖总的平移分量。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OffsetX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translateMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OffsetY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 按缩放、旋转、平移来返回变换分量。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本来第二个参数是可以用 &lt;code class=&quot;highlighter-rouge&quot;&gt;Func&lt;/code&gt; 的，但那样的意义解释起来太费劲，所以改成了委托的定义：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// 为 &amp;lt;see cref=&quot;MatrixToGroup&quot;/&amp;gt; 方法提供变换中心的指定方法。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;scalingFactor&quot;&amp;gt;先进行缩放后进行旋转时，旋转中心的计算可能需要考虑前面缩放后的坐标。此参数可以得知缩放比。&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;绝对坐标的缩放中心和旋转中心。&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScalingCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RotationCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CenterSpecification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时我们就可以得到我们想要的 &lt;code class=&quot;highlighter-rouge&quot;&gt;TransformGroup&lt;/code&gt;，而且 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransformOrigin&lt;/code&gt; 随便设：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransformMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MatrixToGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ScaleAtZeroRotateAtCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransformOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransformGroup&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ScaleAtZeroRotateAtCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTransformOrigin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScaleTransform&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ScaleX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ScaleY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CenterX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTransformOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CenterY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTransformOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RotateTransform&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Angle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CenterX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTransformOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CenterY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTransformOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaleTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotateTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;考虑到前面可以灵活地运用得到的变换分量，我们现在也这么用：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransformMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MatrixToGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalingFactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActualHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TraceShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NoScaleButRotateAtOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransformGroup&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NoScaleButRotateAtOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TransformGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RotateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Angle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateTransform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们的 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransformOrigin&lt;/code&gt; 是随意设的，效果也像下图一样稳定可用。为了直观，我把两种用法放到了一起比较：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-22-traced.gif&quot; alt=&quot;&quot; /&gt;
▲ 设置了 &lt;code class=&quot;highlighter-rouge&quot;&gt;RenderTransformOrigin&lt;/code&gt; 依然有用&lt;/p&gt;
</description>
        <pubDate>Wed, 22 Nov 2017 13:24:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/extract-translation-scaling-rotation-from-matrix.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/extract-translation-scaling-rotation-from-matrix.html</guid>
        
        
        <category>xaml</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>用动画的方式画出任意的路径（直线、曲线、折现）</title>
        <description>&lt;p&gt;WPF/UWP 中提供的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 类可以为我们绘制几乎所有可能的矢量图形。但是，如果这些矢量图形可以以动画的形式播放出来，那将可以得到非常炫酷的演示效果。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我用 Blend 画了我的名字：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-20-00-34-29.png&quot; alt=&quot;walterlv&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Canvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DisplayCanvas&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Row=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.Column=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FrameworkElement.Resources&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Style&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Path&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stretch&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;None&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stroke&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#FF1B6CB0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeThickness&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/FrameworkElement.Resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M501.5,309.22 L510.5,356.22 524,324.72 536,355.72 546,306.22&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M588.5,316.22 C588.5,316.22 561.5,308.72 558,334.72 554.5,360.72 579.5,369.21978 588,357.71985 596.5,346.21993 587.00002,315.22013 588.99999,310.22011 590.49998,326.72017 589.50007,359.22028 597.99998,359.22028&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l1&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M613.5,283.22 C613.5,283.22 607,372.22 623.5,357.22&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t_1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M635.5,317.72 L656.5,316.22&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t_2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M644,285.72 C644,285.72 642.5,334.72 644,345.72 645.5,356.72 657.99343,366.72 661.99155,342.72&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M678.5,329.72 L711.5,327.72 C711.5,327.72 711,306.22 692,307.72 673,309.22 677,325.72 677,336.22 677,346.71999 685.99986,355.21999 692.49989,353.71999 698.99993,352.21999 709.49999,349.22025 709.99999,343.72022&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M725.5,306.72 C740,309.22 733.5,336.22 733.5,344.72 735.5,326.22 726.99993,300.72 763.49829,307.22&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l2&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M786,281.22 C786,281.22 769,372.22 789.5,362.72&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;Data=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M803,308.22 L817,358.22 835.5,310.22&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后将它做成了动画：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-20-draw-path-animatedly.gif&quot; alt=&quot;动画绘制的路径&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而要做到这一点，我们只需要关心 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path&lt;/code&gt; 的两个属性即可：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashArray&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashArray&lt;/code&gt; 是一个包含有很多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt; 的浮点数集合，决定了虚线虚实的变化趋势；&lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset&lt;/code&gt; 是给这个变化趋势添加一个偏移量。&lt;/p&gt;

&lt;p&gt;如果一条直线其长度为 100，粗细为 1，&lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashArray=&quot;5,5&quot;&lt;/code&gt; 表示这段直线用虚线表示绘制；一开始的 5 长度绘制，接下来 5 长度不绘制，再接下来 5 长度绘制，依此类推。在这种情况下，我们再设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset=&quot;1&quot;&lt;/code&gt;，则将虚实的变化延后 1 个长度，即一开始空出 1 长度不绘制后，才接着 5 长度绘制。&lt;/p&gt;

&lt;p&gt;于是，如果我们设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashArray=&quot;100,100&quot;&lt;/code&gt;，那么意味着一开始整条线都绘制，随后在看不见的线条的后面一倍长度上不绘制。我们设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset=&quot;100&quot;&lt;/code&gt; 则意味着将这个绘制整体延后 100 长度，也就是完全看不见。当 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset&lt;/code&gt; 设置成中间值的时候，这跟线条只会绘制一部分。&lt;/p&gt;

&lt;p&gt;于是我们的思路是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashArray&lt;/code&gt;，使其虚实部分都等于线的长度&lt;/li&gt;
  &lt;li&gt;动画设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;StrokeDashOffset&lt;/code&gt;，使其从长度变化到 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是为此制作的动画 XAML：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;CubicEase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EasingFunction.DrawLine&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EaseOut&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Storyboard&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.DrawName&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t_1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:3&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:0.4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;t_2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:3.4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:0.6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:5&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetName=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Storyboard.TargetProperty=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;StrokeDashOffset&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;To=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;BeginTime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:7&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Duration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0:0:1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;EasingFunction=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{StaticResource EasingFunction.DrawLine}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Storyboard&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是我们便可以在 C# 代码中初始化那些 XAML 里算不出来的值（Path 中线的长度）：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawLineStoryboard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Storyboard.DrawName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoutedEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawLineStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;InitializePathAndItsAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DrawLineStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DrawLineStoryboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitializePathAndItsAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shapes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoubleAnimation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProximateLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrokeThickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrokeDashOffset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrokeDashArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoubleCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上述代码中存在一个线长度的估值算法，我们的策略是用多边形近似：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GeometryExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProximateLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Geometry&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFlattenedPathGeometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Figures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Segments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PolyLineSegment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;polyLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 一般的路径会转换成折线。&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;polyLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProximateDistance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LineSegment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 少部分真的是线段的路径会转换成线段。&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProximateDistance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProximateDistance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.webhek.com/post/animated-line-drawing-in-svg.html&quot;&gt;SVG技术入门：如何画出一条会动的线 – WEB骇客&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/10877631/getting-geometry-length&quot;&gt;c# - Getting Geometry length - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 20 Nov 2017 01:07:07 +0000</pubDate>
        <link>https://blog.walterlv.com/post/draw-path-animatedly.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/draw-path-animatedly.html</guid>
        
        
        <category>xaml</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用</title>
        <description>&lt;p&gt;在 WPF 中将一个现成的 Bitmap 位图转换成 ImageSource 用于显示一个麻烦的事儿，因为 WPF 并没有提供多少可以转过来的方法。不过产生 Bitmap 来源却非常多，比如屏幕截图、GDI 图、数组或其它非托管框架生成的图片。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;WPF 官方提供了一种方法，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap()&lt;/code&gt; 方法。官方解释称这是托管和非托管位图相互转换所用的方法。然而此方法有一个很严重的弊端——每次都会生成全新的位图，即便每次 &lt;code class=&quot;highlighter-rouge&quot;&gt;DeleteObject&lt;/code&gt; 之后，内存依然不会即时释放。&lt;/p&gt;

&lt;p&gt;DeleteObject：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DllImport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gdi32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DeleteObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;DeleteObject 的指针源于 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bitmap.GetHbitmap()&lt;/code&gt; 方法，且得到的指针会作为 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap()&lt;/code&gt; 的参数之一。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在持续输出图像的时候（例如播放 Gif 图、持续显示屏幕截图等）不及时释放内存非常致命！为了防止重复创建图片，&lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 似乎成了比较好的选择。&lt;/p&gt;

&lt;p&gt;但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteableBitmap&lt;/code&gt; 没有提供与位图 Bitmap 的互操作。然而它们都提供了像素操作。&lt;/p&gt;

&lt;p&gt;于是，我们考虑内存拷贝来完成转换，代码如下：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WriteableBitmapExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CopyFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WriteableBitmap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bitmap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;暂时只支持相同尺寸图片的复制。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitsPerPixel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rBitmapData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LockBits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ImageLockMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PixelFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MemoryCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rBitmapData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scan0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BackBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDirtyRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Int32Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnlockBits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rBitmapData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我写了一个持续不断截取屏幕并输出显示的控件，在我的 The New Surface Pro 2736*1826 分辨率下内存一直保持 168M 从不变化。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-09-23-25-23.png&quot; alt=&quot;内存占用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个方法的简化空间还非常大，比如，如果数据源是一个一次申请不断修改的数组，那么连 &lt;code class=&quot;highlighter-rouge&quot;&gt;Bitmap&lt;/code&gt; 都可以不需要了，直接拷贝数组空间即可。我的朋友林德熙为此将这段代码简化得只剩下几行代码了：&lt;a href=&quot;https://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%AE%89%E5%85%A8%E4%BB%A3%E7%A0%81%E5%BF%AB%E9%80%9F%E4%BB%8E%E6%95%B0%E7%BB%84%E8%BD%AC-WriteableBitmap.html&quot;&gt;WPF 使用不安全代码快速从数组转 WriteableBitmap - 林德熙&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Fri, 10 Nov 2017 06:42:45 +0000</pubDate>
        <link>https://blog.walterlv.com/post/convert-bitmap-to-imagesource-using-unsafe-method.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/convert-bitmap-to-imagesource-using-unsafe-method.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>Visual Studio 也开始支持 Ctrl 点击跳转了，于是需要解决跟 ReSharper 的冲突</title>
        <description>&lt;p&gt;微软在 2017年10月9日 发布了 Visual Studio 2017 version 15.4.0。而这个版本带来了大家期待已久的 Ctrl+Click 跳转到定义的功能。然而……ReSharper 也是这样的快捷键，也是这样的功能！！！&lt;/p&gt;

&lt;p&gt;居然冲突了啊，怎么办？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;这里可以阅读发布日志：&lt;a href=&quot;https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes&quot;&gt;Visual Studio 2017 15.4 Release Notes&lt;/a&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Editor&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;We added the popular Productivity Power Tools navigation feature &lt;strong&gt;Control Click Go To Definition&lt;/strong&gt; to the core Visual Studio product.
      &lt;ul&gt;
        &lt;li&gt;For supported languages (currently C#, VB and Python, with more languages coming in future releases), holding down the &lt;strong&gt;Ctrl&lt;/strong&gt; key will allow you to click on a symbol in the Visual Studio editor and navigate to its definition.&lt;/li&gt;
        &lt;li&gt;If you prefer to keep the older &lt;strong&gt;Ctrl+Click&lt;/strong&gt; word selection behavior, you can control the feature’s key usage via &lt;strong&gt;Tools &amp;gt; Options &amp;gt; Text Editor &amp;gt; General &amp;gt; Enable mouse click to perform Go To Definition&lt;/strong&gt;, which lets you select other modifier keys, or turn off the feature if you wish.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;所以 Visual Studio 和 ReSharper 开始冲突，具体表现为，点击跳转到定义后，如果鼠标在转到定义之后刚好还落在另一个单词上，那么还会跳转到那个新的单词，非常恶心！&lt;/p&gt;

&lt;p&gt;只恶心自己就好了，为了防止恶心到大家，我找了几天，终于分别找到了 Visual Studio 和 ReSharper 两者的设置项。如下图，关掉一个就好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-07-15-54-22.png&quot; alt=&quot;Visual Studio 和 ReSharper 中的设置&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Nov 2017 07:55:11 +0000</pubDate>
        <link>https://blog.walterlv.com/post/resolve-ctrl-click-confiliction-between-vs-and-resharper.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/resolve-ctrl-click-confiliction-between-vs-and-resharper.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>修复 WPF 窗口在启动期间短暂的白底显示</title>
        <description>&lt;p&gt;不管你做的 WPF 窗口做得多么简单，是否总感觉启动的那一瞬间窗口内是白白的一片？是否试过无数偏方黑科技，但始终无法解决？&lt;/p&gt;

&lt;p&gt;本文将介绍一种简单的方法来彻底解决这个问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;看看下面这张图，你便能知道本文要解决的问题是否跟你希望解决的是同一个问题：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-03-wpf-window-show-with-white.gif&quot; alt=&quot;启动期间显示白色&quot; /&gt;&lt;/p&gt;

&lt;p&gt;是否发现窗口启动期间，窗口中的内容是白色的呢？&lt;/p&gt;

&lt;p&gt;然而我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 超级简单：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;星i&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv's demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;24&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Thin&quot;&lt;/span&gt;
                   &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;这个问题在网上 Google 搜索结果上已发现有很多讨论：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/bdb414fe-9abb-408c-8935-486e1795755b/wpf-window-with-black-background-flashes-white-when-first-shown?forum=wpf&quot;&gt;WPF Window with black background flashes white when first shown&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/vstudio/en-US/dd8477a6-a7bc-4171-9547-f86ed722d95d/white-screen-before-loading-main-window-contents?forum=wpf&quot;&gt;White screen before loading main window contents&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/35120487/6233938&quot;&gt;How can I avoid flicker in a WPF fullscreen app?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而基本上观点都是相似的：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;这是 WPF 的已知 BUG（this is a known issue in WPF）&lt;/li&gt;
  &lt;li&gt;可以先设置窗口 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowState=&quot;Minimized&quot;&lt;/code&gt;，然后等 &lt;code class=&quot;highlighter-rouge&quot;&gt;Loaded&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;ContentRendered&lt;/code&gt; 之后再设回 &lt;code class=&quot;highlighter-rouge&quot;&gt;Normal&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;Maximized&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;经过多次尝试，甚至都改掉了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Template&lt;/code&gt; 都无法解决这个问题。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;x:Class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv.Demo.MainWindow&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:x=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:mc=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns:local=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clr-namespace:Walterlv.Demo&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mc:Ignorable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;星i&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Window.Template&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;ControlTemplate&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;TargetType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Window&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ContentPresenter/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ControlTemplate&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window.Template&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Border&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Background=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Teal&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;TextBlock&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Text=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;walterlv's demo&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Foreground=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;White&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;24&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FontWeight=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Thin&quot;&lt;/span&gt;
                   &lt;span class=&quot;na&quot;&gt;TextAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;VerticalAlignment=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/TextBlock&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;但是！！！&lt;/strong&gt;发现使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 定制窗口非客户区的时候，此问题就不再出现了！！！&lt;/p&gt;

&lt;p&gt;也就是说，此问题在微软彻底解决之前，也是有规避方案的！——那就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt;！&lt;/p&gt;

&lt;p&gt;这是效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-11-03-wpf-window-show-without-white.gif&quot; alt=&quot;启动期间没有显示白色&quot; /&gt;&lt;/p&gt;

&lt;p&gt;做法就是给 &lt;code class=&quot;highlighter-rouge&quot;&gt;Window&lt;/code&gt; 设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowChrome&lt;/code&gt; 附加属性：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;无需额外设置任何值，即可修复此问题（不过此时在 Visual Studio 中调试可能发现启动动画丢失）。&lt;/p&gt;

&lt;p&gt;但是，由于此时开始能够在非客户区（NonClientArea）显示控件了，所以可能需要自己调整一下视觉效果。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WindowChrome&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;GlassFrameThickness=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0 31 0 0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CornerRadius=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;UseAeroCaptionButtons=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WindowChrome.WindowChrome&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 03 Nov 2017 15:08:46 +0000</pubDate>
        <link>https://blog.walterlv.com/post/fix-white-screen-when-wpf-window-launching.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/fix-white-screen-when-wpf-window-launching.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>为 Web 页面添加 iPhone 固定标签页的图标</title>
        <description>&lt;p&gt;我曾经将一个 Web 标签页固定到 iPhone 的主屏幕上，发现居然有一个图标。当时没有留意，可直到今天发现我的博客页面在我的 iPhone 主屏幕上显示一片空白后，才想起来原来还可以自定义图标。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;将 Web 页面图标固定到主屏幕：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-28-pin-a-web-tab.jpg&quot; alt=&quot;固定到主屏幕&quot; /&gt;&lt;/p&gt;

&lt;p&gt;方法非常简单，只需要在我 html 里的 head 中加入以下代码即可：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple-touch-icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;图标的链接.svg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于我的博客，也就是这篇文章来说，我用了我的个人头像作为图标。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/logo.png&quot; alt=&quot;我的头像&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最终固定到 iPhone 主屏幕的效果还不错！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-28-view-pinned-tab-icon.jpg&quot; alt=&quot;最终效果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/pinnedTabs/pinnedTabs.html&quot;&gt;Creating Pinned Tab Icons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html&quot;&gt;Configuring Web Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 27 Oct 2017 16:16:52 +0000</pubDate>
        <link>https://blog.walterlv.com/post/add-icon-for-pinned-tab-icons.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/add-icon-for-pinned-tab-icons.html</guid>
        
        
        <category>web</category>
        
        <category>ios</category>
        
      </item>
    
      <item>
        <title>使用 ExceptionDispatchInfo 捕捉并重新抛出异常</title>
        <description>&lt;p&gt;当你跑起了一个异步线程，并用 &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; 异步等待时，有没有好奇为什么能够在主线程 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 到异步线程的异常？&lt;/p&gt;

&lt;p&gt;当你希望在代码中提前收集好异常，最后一并把收集到的异常抛出的时候，能不能做到就像在原始异常发生的地方抛出一样？&lt;/p&gt;

&lt;p&gt;本文介绍 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt;，专门用于重新抛出异常。它在 .NET Framework 4.5 中首次引入，并原生在 .NET Core 和 .NET Standard 中得到支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;先探索为什么需要重新抛出异常，再了解如何最佳地重新抛出异常。如果你只希望了解 &lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt;，请直接从以下导航中点击跳转到最后一节。&lt;/p&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;重新抛出异常&quot;&gt;重新抛出异常&lt;/h2&gt;

&lt;p&gt;说起重新抛出异常，你是否会认为就是写出如下代码？&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoButExceptionsMayOccur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里进行抢救。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 永远不要写出下面这句代码！（Don't write the code below forever!）&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了防止这段代码被意外复制出去危及项目，我特地在注释中标明了永远不应该直接写出 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw ex&lt;/code&gt; 这样的句子！&lt;/p&gt;

&lt;p&gt;这是因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 语句会为异常的实例填充调用栈信息，范围为 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 的地方开始，到 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 的地方结束。也就是说，在异常刚刚发生的时候，也就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoButExceptionsMayOccur&lt;/code&gt; 里面的某一个调用会成为调用栈的起点，上面写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 所在的函数会成为调用栈的终点。然而，一旦在 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 中写出了 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw ex&lt;/code&gt; 这样的语句，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;ex&lt;/code&gt; 中的调用栈将会被重写，范围从这一句 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw&lt;/code&gt; 开始，到外面能 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 的地方为止。&lt;/p&gt;

&lt;p&gt;具体说来，假设上面那段代码出现在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt; 方法中，里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DoButExceptionsMayOccur&lt;/code&gt; 调用了方法 &lt;code class=&quot;highlighter-rouge&quot;&gt;Inner&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;Inner&lt;/code&gt; 中发生了异常；而 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outer&lt;/code&gt; 调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt; 方法，&lt;code class=&quot;highlighter-rouge&quot;&gt;Outer&lt;/code&gt; 中也 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 了异常；即整个调用链为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outer&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DoButExceptionsMayOccur&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Inner&lt;/code&gt;。那么，当刚刚 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 到异常时，&lt;code class=&quot;highlighter-rouge&quot;&gt;ex&lt;/code&gt; 的调用栈为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;DoButExceptionsMayOccur&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Inner&lt;/code&gt;，而如果写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;throw ex&lt;/code&gt;，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outer&lt;/code&gt; 中将只能发现调用栈为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Outer&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Test&lt;/code&gt;，丢失了内部真正出错的原因，这对诊断和修复异常非常不利！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-23-21-31-48.png&quot; alt=&quot;两次抛出异常时收获的调用栈&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果只是为了解决上述文字中所说的问题，其实只需要去掉那个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ex&lt;/code&gt; 即可，即：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoButExceptionsMayOccur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 在这里进行抢救。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，有时候这个异常并不直接从这里抛出（例如后台线程），或者说我们期望这是一个分步骤收集的异常（例如遍历）。这两种情况都有一个共同特点，就是重新抛出的地方根本就不在 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 的地方。&lt;/p&gt;

&lt;p&gt;后台线程的例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 这个 try-catch 块将在另一个线程执行。&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoButExceptionsMayOccur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 重新抛出异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;收集异常的例子：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoButExceptionsMayOccur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 重新抛出异常。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;使用内部异常&quot;&gt;使用内部异常&lt;/h2&gt;

&lt;p&gt;.NET Framework 早期就提供了内部异常功能，专为解决保留调用栈而重新抛出异常而生。上面两段代码标记为&lt;code class=&quot;highlighter-rouge&quot;&gt;// 重新抛出异常。&lt;/code&gt;的注释部分改为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 对应第一种情况。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;XxxException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 对应第二种情况。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AggregateException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是两边的调用栈就被分别保留在了多个不同的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 实例中。然而看异常总要一层层点开查看，始终不便。尤其是从产品中收集异常时，如何在异常分析系统中显示和分析也是个问题。&lt;/p&gt;

&lt;h2 id=&quot;exceptiondispatchinfo&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ExceptionDispatchInfo&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;如果将第一种情况写为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，这时外面的方法再 &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; 异常，则会从外层直接看到里层，只在中间插入了一段文字，却看起来就像直接从原始出处抛出一样。&lt;/p&gt;

&lt;p&gt;第二种情况写为：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExceptionDispatchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AggregateException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用这种方式，你看到的调用栈将是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-23-22-22-30.png&quot; alt=&quot;使用 ExceptionDispatchInfo 的调用栈&quot; /&gt;&lt;/p&gt;

&lt;p&gt;至于多个异常的情况，那就只能使用内部异常来处理了。&lt;/p&gt;

&lt;p&gt;而这些，正是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; 管理异步线程异常时采用的策略——单个异常直接在调用线程直接抛出，多个异常抛出 &lt;code class=&quot;highlighter-rouge&quot;&gt;AggregateException&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Oct 2017 23:38:48 +0000</pubDate>
        <link>https://blog.walterlv.com/post/exceptiondispatchinfo-capture-throw.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/exceptiondispatchinfo-capture-throw.html</guid>
        
        
        <category>dotnet</category>
        
        <category>dotnet-core</category>
        
        <category>dotnet-standard</category>
        
        <category>csharp</category>
        
      </item>
    
      <item>
        <title>如何组织一个同时面向 UWP/WPF/.Net Core 控制台的 C# 项目解决方案</title>
        <description>&lt;p&gt;希望写一个小型工具，给自己和需要的人。考虑到代码尽可能的复用，我准备采用 .Net Standard 来编写大多数核心代码，并基于 .Net Core 编写跨平台控制台入口，用 WPF 编写桌面端 UI 入口，用 UWP 作为可上架商店的 UI 入口，然后用 Shared Project 共享 WPF 和 UI 的多数 UI 入口代码。&lt;/p&gt;

&lt;p&gt;阅读本文将了解到如何在尽可能复用代码的情况下组织这样的 C# 解决方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;工具型项目选择了控制台&quot;&gt;工具型项目，选择了控制台&lt;/h2&gt;

&lt;p&gt;用 WPF 开发桌面 UI，因为其有强大的 .NET Framework 库在背后支持，外加方便而功能齐全的 XAML 开发环境，在用 C# 进行桌面应用程序开发的时候不失为一种优秀的选择。但微软却并不怎么重视 WPF，而一直投入较大资源在半死不活的 UWP 上，导致 WPF 现在有非常多的坑是在 UWP 上才解的。然而，微软却并没有好好运营 UWP，以至于其开发者急剧减少，再在上面投入太多精力投入产出比显得太低。&lt;/p&gt;

&lt;p&gt;.NET Framework 是个优秀的框架，可是与 Windows 桌面端绑得太死，以至于在当下多平台发展得都不错的情况下失去了大多数的竞争力。但是 .NET Core 解决了这个问题。然而谈到 UI 的跨平台，就是一个巨大的投入和难以见底的坑，以至于基于 .NET Core 且跨平台的 UI 框架目前依然没有出现。&lt;/p&gt;

&lt;p&gt;毕竟只是工具型项目，并不想去动用大型 UI 框架 Xamarin/Unity，以至于写一个 .NET Core 控制台程序成了小型工具型项目的最佳解决方案了。&lt;/p&gt;

&lt;p&gt;工具型项目是任务导向的，能完成任务为最终目的。控制台与配置文件的配合不仅足以完成任务，还为自动化或其他工具集成提供了方便。这里提供 UI 只是为了方便此工具用户的初学使用和理解。&lt;/p&gt;

&lt;h2 id=&quot;组织一个-c-解决方案&quot;&gt;组织一个 C# 解决方案&lt;/h2&gt;

&lt;p&gt;我们总共涉及到的 Visual Studio 项目类型有这五个：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;类库(.NET Standard)&lt;/li&gt;
  &lt;li&gt;共享项目&lt;/li&gt;
  &lt;li&gt;控制台应用(.NET Core)&lt;/li&gt;
  &lt;li&gt;WPF 应用(.NET Framework)&lt;/li&gt;
  &lt;li&gt;空白应用(通用 Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;.NET Standard 和共享项目是默认就装上的，但其他三个却不是。需要在 Visual Studio 安装界面中额外勾选：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;用于安装通用 Windows 项目，如果你对此不感兴趣，忽略即可&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-21-10-20-33.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;用于安装 WPF 应用，如果你对此不感兴趣，忽略即可&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-21-10-21-35.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;用于安装 .NET Core 项目，这是跨平台的重点，建议安装&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-21-10-22-40.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 Visual Studio 中创建一个解决方案的时候依次添加这五种项目。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;我们的主要逻辑代码全在 .NET Standard 项目中。这里包含了完整的功能实现，可以脱离其他四种实现完整功能。&lt;/li&gt;
  &lt;li&gt;.NET Core 控制台项目仅仅作为入口，引用 .NET Standard 的项目，将用户输入的命令转为具体的函数调用。&lt;/li&gt;
  &lt;li&gt;共享项目的代码主要是 UI 或 UI 辅助代码，例如控制 UI 的逻辑和 ViewModel。&lt;/li&gt;
  &lt;li&gt;UWP 和 WPF 项目仅包含 UI（XAML）和必要的不一致的 UI 控制逻辑，通过链接的方式将共享项目中的代码引入&lt;a href=&quot;/visualstudio/2016/08/01/share-code-with-add-as-link.html&quot;&gt;如何链接？&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;其他的工具库当然也是需要的，但为了通用，建议优先选择 .NET Standard 的库。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样，项目在 Visual Studio 中看起来大概是这样的：&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-21-11-19-16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 21 Oct 2017 03:20:54 +0000</pubDate>
        <link>https://blog.walterlv.com/post/organize-csharp-project-targeting-multiple-platforms.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/organize-csharp-project-targeting-multiple-platforms.html</guid>
        
        
        <category>dotnet</category>
        
        <category>dotnet-core</category>
        
        <category>dotnet-standard</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>UWP 和 WPF 不同，ListView 中绑定的集合修改顺序时，UI 的刷新规则</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 中有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 方法，而这个方法在其他类型的集合中是很少见的。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 主要用于绑定，涉及到 UI 更新，而 UI 更新普遍比普通的集合修改慢了不止一个数量级，所以可以大胆猜想，&lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 的存在是为了提升 UI 刷新性能。&lt;/p&gt;

&lt;p&gt;然而事实真是这样的吗？&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;试验&quot;&gt;试验&lt;/h2&gt;

&lt;p&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 用于 UI 绑定的目前只有 UWP 和 WPF，于是我写了两个 App 来验证这个问题。代码已上传 GitHub &lt;a href=&quot;https://github.com/walterlv/ListViewBindingDemo-for-ItemsMove&quot;&gt;walterlv/ListViewBindingDemo for ItemsMove&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;验证方式主要看两个点：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;UI 元素的 Hash 值有没有更改，以便了解 UWP 或 WPF 框架是否有为此移动的数据创建新的 UI。&lt;/li&gt;
  &lt;li&gt;UI 元素的焦点有没有变化，以便了解 UWP 或 WPF 是否将此 UI 元素移出过视觉树。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;结果如下图：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;在 UWP 中，移动数据的元素焦点没有改变，Hash 值也没有改变。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-20-uwp-items-move-1.gif&quot; alt=&quot;UWP 中看被移动的元素&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 UWP 中，未被移动数据的元素 Hash 值没有改变。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-20-uwp-items-move-2.gif&quot; alt=&quot;UWP 中看未被移动的元素&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 WPF 中，移动数据的元素焦点丢失，Hash 值已经改变。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-20-wpf-items-move-1.gif&quot; alt=&quot;WPF 中看被移动的元素&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 WPF 中，未被移动数据的元素 Hash 值没有改变。&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-10-20-wpf-items-move-2.gif&quot; alt=&quot;WPF 中看未被移动的元素&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;猜想&quot;&gt;猜想&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;UWP 真的对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 操作有优化，根本就没有将移动数据的元素移除视觉树。&lt;/li&gt;
  &lt;li&gt;WPF 似乎并没有对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 操作进行优化，因为 Hash 值都变了，直接就是创建了个新的。几乎等同于将原来的 UI 元素移除之后再创建了一个新的。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;调查&quot;&gt;调查&lt;/h2&gt;

&lt;p&gt;.Net Standard 统一了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 的 API，所以 UWP 和 WPF 这些基本的 API 是一样的。由于 .NET Framework 发布了源代码，.Net Core 直接开源，所以这两者的代码我们都能翻出来。&lt;/p&gt;

&lt;p&gt;这是 [Net Framework 版的 ObservableCollection&lt;T&gt;.MoveItem](http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs,270a83d222656b02)&lt;/T&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Called by base class ObservableCollection&amp;amp;lt;T&amp;amp;gt; when an item is to be moved within the list;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// raises a CollectionChanged event to any listeners.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;CheckReentrancy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;OnPropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexerName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;OnCollectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyCollectionChangedAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是 [.Net Core 版的 ObservableCollection&lt;T&gt;.MoveItem](https://github.com/dotnet/corefx/blob/master/src/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs)&lt;/T&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// Called by base class ObservableCollection&amp;amp;lt;T&amp;amp;gt; when an item is to be moved within the list;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// raises a CollectionChanged event to any listeners.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoveItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;CheckReentrancy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;OnIndexerPropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;OnCollectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyCollectionChangedAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;好吧，微软真省事儿，不止代码中的每个字母都相同，就连注释都一样……&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MoveItem&lt;/code&gt; 所做的就是在旧的位置移除元素，并将其插入到新的位置。于是，优化的重心就在于引发 &lt;code class=&quot;highlighter-rouge&quot;&gt;CollectionChanged&lt;/code&gt; 事件时传入的参数了，都是传入 &lt;code class=&quot;highlighter-rouge&quot;&gt;NotifyCollectionChangedAction.Move&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;由于 UWP 没有开源，从源码级别我们只能分析 WPF 为此枚举所做的事情。在 WPF 中，&lt;code class=&quot;highlighter-rouge&quot;&gt;ListView&lt;/code&gt; 为此所做的判断仅一处，就是其基类 &lt;code class=&quot;highlighter-rouge&quot;&gt;ItemsControl&lt;/code&gt; 类的 &lt;code class=&quot;highlighter-rouge&quot;&gt;AdjustItemInfos&lt;/code&gt; 方法。然而此方法内部对 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 的实现几乎就是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Remove&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt; 的叠加。&lt;/p&gt;

&lt;p&gt;但是 UWP 中我们可以做更多的试验。比如我们直接移除掉原来的一项，然后延迟再添加一个新的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditingText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者我们直接添加一个跟原来不同的项：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditingText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EditableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这时运行发现，焦点确实移除了，但 HashCode 依然是原来的 HashCode。基本可以确定，UWP 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ListBox&lt;/code&gt; 做了更多的优化，在根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;DataTemplate&lt;/code&gt; 生成控件时，一直在重用之前已经生成好的控件。&lt;/p&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;

&lt;p&gt;UWP 比 WPF 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 的集合操作进行了更好的性能优化，在添加、删除、移动时会重用之前创建好的控件。而在 WPF 中，则简单地创建和销毁这些控件——即便调用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; 专有的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 方法也没有做更多的优化。&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Oct 2017 00:14:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/binded-items-move-behavior-in-listview.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/binded-items-move-behavior-in-listview.html</guid>
        
        <category>UWP</category>
        
        <category>WPF</category>
        
        <category>ListView</category>
        
        <category>Binding</category>
        
        <category>Items</category>
        
        
        <category>dotnet</category>
        
        <category>wpf</category>
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>GitHub 的 Pull Request 和 GitLab 的 Merge Request 有区别吗？</title>
        <description>&lt;p&gt;在 GitHub 上混久了，对 Pull Request 就……；在 GitLab 上混久了，对 Merge Request 就……然而它们之间有不同吗？为什么要用两个不同的名称？&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;要追溯这两个名称，需要追溯 GitHub 和 GitLab 引以为傲的 git 工作流。这也是本文参考链接中一定要附上 GitLab 工作流的重要原因。&lt;/p&gt;

&lt;p&gt;众所周知 git 是一个&lt;strong&gt;分布式&lt;/strong&gt;的版本管理系统，但为了团队成员之间能够高效地协作，必须有至少一个服务器用于给团队所有成员之间同步代码。而这一点又有点类似于集中式的版本管理。&lt;/p&gt;

&lt;p&gt;对于项目的核心成员，集中式版本管理和分布式版本管理贡献代码的方式并没有多大差异（这里不要纠结个人使用层面的差异，只谈论为仓库贡献代码的方式）。但对于非项目核心成员来说，集中式的版本管理就非常痛苦了，因为他们找不到方式来提交自己的代码（请忽略低效的发邮件补丁吧……）。然而分布式版本管理则解决了这个问题：非项目核心成员可以克隆仓库，这样就得到了一个自己具有完全读写权限的仓库，贡献的代码可以完全同步到这个具有完全读写权限的仓库中。&lt;/p&gt;

&lt;p&gt;为了让非核心成员提交的代码被核心成员接纳，非核心成员会向核心成员提出“申请（Request）”去自己的仓库指定分支中“拉取(pull)”最新的修改，这便是 Pull Request 的来源。&lt;/p&gt;

&lt;p&gt;那么 Merge Request 又是什么呢？GitLab 对此的解释是——一样的，没有区别。Merge 只是在强调最后的那个动作“合并（Merge）”。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub、Bitbucket 和码云（Gitee.com）选择 Pull Request 作为这项功能的名称&lt;/li&gt;
  &lt;li&gt;GitLab 和 Gitorious 选择 Merge Request 作为这项功能的名称&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/ce/workflow/gitlab_flow.html&quot;&gt;GitLab Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/22199432/pull-request-vs-merge-request&quot;&gt;git - Pull request vs Merge request - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://git.mydoc.io/&quot;&gt;码云平台帮助文档_V1.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 12 Oct 2017 01:13:40 +0000</pubDate>
        <link>https://blog.walterlv.com/post/git/pull-request-merge-request.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/git/pull-request-merge-request.html</guid>
        
        <category>git</category>
        
        <category>merge</category>
        
        <category>pull</category>
        
        <category>request</category>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>查询已连接 Wi-Fi 的密码（入门和进阶两种方法）</title>
        <description>&lt;p&gt;新买了手机或者带着朋友去好玩的地方，我自己的 Windows 10 设备连接上了 Wi-Fi，朋友也希望连接上，但是我忘记了密码怎么办？&lt;/p&gt;

&lt;hr /&gt;

&lt;p id=&quot;toc&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;进阶篇&quot;&gt;进阶篇&lt;/h2&gt;

&lt;p&gt;其实重点并不是解决问题，而是解决问题的过程；所以使用命令行来解决这个问题当然更加炫酷一些，当然要第一个讲啦！让其他人投来羡慕的目光吧！&lt;/p&gt;

&lt;p&gt;总共两条命令：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;netsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wlan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;profiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-20-48-55.png&quot; alt=&quot;netsh wlan show profiles&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图是第一条命令执行的结果，其实我们只是为了得到已记住的所有 Wi-Fi 名称而已，如果你知道名称，这一步可以省略。&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;netsh&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wlan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;profiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;walterlv&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-20-51-53.png&quot; alt=&quot;netsh wlan show profiles name=&amp;quot;walterlv&amp;quot; key=Clear&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二条命令就是查看 &lt;code class=&quot;highlighter-rouge&quot;&gt;walterlv&lt;/code&gt; 网络的信息。其中 &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; 换成你想查看的任何已记住的网络，&lt;code class=&quot;highlighter-rouge&quot;&gt;key&lt;/code&gt; 设置为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Clear&lt;/code&gt; 是为了明文显示密码。我的 Wi-Fi 密码在图中可以看得到，被设置成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;lvyi1009&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;入门篇&quot;&gt;入门篇&lt;/h2&gt;

&lt;p&gt;如果你觉得上面的方法太装了，想朴素一些，那么只需要点点鼠标即可。&lt;/p&gt;

&lt;p&gt;打开网路和共享中心，然后点击正在连接的网络名称。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-19-48-43.png&quot; alt=&quot;打开网络和共享中心&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-20-41-55.png&quot; alt=&quot;点击正在连接的网络名称&quot; /&gt;&lt;/p&gt;

&lt;p&gt;点击“无线属性”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-20-54-39.png&quot; alt=&quot;无线属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-10-09-20-58-19.png&quot; alt=&quot;无线网络属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;显示字符就能看到密码了。&lt;/p&gt;

&lt;p&gt;不过这种方法只能看到当前正在连接的 Wi-Fi 网络的密码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.itechtics.com/2-ways-find-saved-wifi-passwords-windows-10/&quot;&gt;2 Ways To Find All Saved Wifi Passwords In Windows 10&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 09 Oct 2017 13:01:31 +0000</pubDate>
        <link>https://blog.walterlv.com/post/windows/find-wifi-password.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/windows/find-wifi-password.html</guid>
        
        <category>Windows</category>
        
        <category>Wi-Fi</category>
        
        <category>wifi</category>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>CaptureMouse/CaptureStylus 可能会失败</title>
        <description>&lt;p&gt;在 WPF 中，如果我们要做拖动效果，通常会调用一下 &lt;code class=&quot;highlighter-rouge&quot;&gt;CaptureMouse&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;CaptureStylus&lt;/code&gt; 以便当鼠标或手指离开控件的时候依然能够响应 &lt;code class=&quot;highlighter-rouge&quot;&gt;Move&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Up&lt;/code&gt; 事件。不知有没有注意到这两个函数其实是有 &lt;code class=&quot;highlighter-rouge&quot;&gt;bool&lt;/code&gt; 返回值的？——是的，它们可能会失败。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在调试一个项目代码的时候，我就发现了这种失败，观察返回值确实是 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;，然而为什么呢？&lt;/p&gt;

&lt;p&gt;查看 &lt;a href=&quot;http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Mouse.cs,679caaf70ff0c397&quot;&gt;.NET Framework 的源码&lt;/a&gt; 我们发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;CaptureMouse&lt;/code&gt; 最终调到了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.Capture&lt;/code&gt; 方法：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mouse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrimaryDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后一步步调到了 &lt;a href=&quot;http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/MouseDevice.cs,35f00e7026f6c3d7&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bool Capture(IInputElement element, CaptureMode captureMode)&lt;/code&gt;&lt;/a&gt;，而其中对是否可 &lt;code class=&quot;highlighter-rouge&quot;&gt;Capture&lt;/code&gt; 的关键性影响代码就在这个方法内部。为了便于理解，我把他改成了下面这样，是等价的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CanCapture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IInputElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentElement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIElement3D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e3D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e3D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsVisible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e3D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;这段代码感兴趣可以拿走，以便在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Capture&lt;/code&gt; 之前可以进行预判。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;从这段代码可以很清楚地知道，如果元素已不可见 (&lt;code class=&quot;highlighter-rouge&quot;&gt;IsVisible&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;) 或者不可用（&lt;code class=&quot;highlighter-rouge&quot;&gt;IsEnabled&lt;/code&gt; 为 &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;），则不可 &lt;code class=&quot;highlighter-rouge&quot;&gt;Capture&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;以此为线索，果然发现调试的项目中在 &lt;code class=&quot;highlighter-rouge&quot;&gt;MouseDown&lt;/code&gt; 事件里把元素隐藏了。&lt;/p&gt;

&lt;p&gt;总结：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果元素不可见或不可用，则 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.Capture&lt;/code&gt; 会失败。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;顺便还发现一个问题，&lt;code class=&quot;highlighter-rouge&quot;&gt;Stylus.Capture(IInputElement)&lt;/code&gt; 中居然直接调用的是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Mouse.Capture(IInputElement)&lt;/code&gt;。&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Oct 2017 11:05:56 +0000</pubDate>
        <link>https://blog.walterlv.com/post/wpf/capture-mouse-failed.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/wpf/capture-mouse-failed.html</guid>
        
        <category>CaptureMouse</category>
        
        <category>CaptureStylus</category>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>彻底删除 Git 仓库中的文件避免占用大量磁盘空间</title>
        <description>&lt;p&gt;今天早上照常 &lt;code class=&quot;highlighter-rouge&quot;&gt;git fetch --prune&lt;/code&gt; 获取大家写的代码，发现需要好长时间，但没怎么在意。直到下午小伙伴们才发现居然 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 了一个多 GB！询问才发现小伙伴 &lt;a href=&quot;http://niuyanjie.gitee.io/blog/&quot;&gt;JAKE&lt;/a&gt;（其实我是在推荐博客）误传了 1.47GB 的垃圾文件。关键是等发现时，&lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支上已经有 20+ 个基于这个文件的新提交了。&lt;/p&gt;

&lt;p&gt;小伙伴说“不要紧，现在我已经删除它了！”突然一阵后背发凉，我们才 900M 的仓库肯定一下子飙到了 2000+M，必须马上处理之。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果你想知道到底发生了什么造成突然多出这么大的文件，请阅读：&lt;a href=&quot;http://niuyanjie.gitee.io/blog/%E4%B8%80%E4%B8%AA%E5%8E%8B%E7%BC%A9%E5%8C%85%E5%BC%95%E5%8F%91%E7%9A%84%E8%A1%80%E6%A1%88/&quot;&gt;一个压缩包引发的血案 - niuyanjie’s blog&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;问题的本质和解决思路&quot;&gt;问题的本质和解决思路&lt;/h2&gt;

&lt;p&gt;有的小伙伴问了，为何删了也会占用仓库空间？这是因为 Git 会记录历史的每一次提交，而且提交中包含了完整的数据。如果有一次提交中增加了一个大文件，即便后面删除了此文件再提交，之前增加文件的提交也在历史中。由于 Git 是分布式仓库，每个人都克隆了完整的 Git 仓库，包含完整的历史，于是这个大文件对空间的吞噬其实影响着每一个 Git 仓库的副本。&lt;/p&gt;

&lt;p&gt;所以，解决问题的思路其实是——让整个 Git 历史中不存在这个文件！&lt;/p&gt;

&lt;p&gt;一种方法是修改有问题的提交，使这个提交中不包含对此文件的修改记录；另一种方法就是将这个提交从整个提交历史记录中干掉。&lt;/p&gt;

&lt;p&gt;后文介绍的方法中，“推荐的方法”属于前者，“不推荐的方法”属于后者。&lt;/p&gt;

&lt;h2 id=&quot;推荐的方法&quot;&gt;推荐的方法&lt;/h2&gt;

&lt;p&gt;感谢小伙伴 &lt;a href=&quot;http://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 的帮助，帮我找到了一篇非常有价值的博客：&lt;a href=&quot;http://www.cnblogs.com/shines77/p/3460274.html&quot;&gt;Git如何永久删除文件(包括历史记录) - shines77 - 博客园&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;强烈推荐只阅读上面那篇文章而不要阅读本文，因为本文真正用到的方法比上面的更 low。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;看到 &lt;code class=&quot;highlighter-rouge&quot;&gt;filter-branch&lt;/code&gt;，突然想到前几天给 Git 仓库补提交一个文件用的是同一个方法：&lt;a href=&quot;/git/2017/09/13/add-file-to-whole-git-repository.html&quot;&gt;如何向整个 Git 仓库补提交一个文件&lt;/a&gt;，那既然如此，里面各种参数的含义也就读(si)得(dong)懂(fei)了(dong)……（/暗笑）。&lt;/p&gt;

&lt;p&gt;命令如下：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--index-filter&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'git rm --cached --ignore-unmatch path-to-your-remove-file'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prune-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tag-name-filter&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;把 &lt;code class=&quot;highlighter-rouge&quot;&gt;path-to-your-remove-file&lt;/code&gt; 改成那个误传的文件后，写下命令准备运行……运行……运行……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-18-git-for-windows-high-cpu.png&quot; alt=&quot;高 CPU 占用&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后过了十多分钟还不到 5% 的进度……&lt;/p&gt;

&lt;p&gt;算了，放弃吧……我们这种数万次提交，900M 的大仓库，这样的命令似乎玩不起啊……这个命令一定还能用，比如指定版本区间什么的，但是我不会……&lt;/p&gt;

&lt;h2 id=&quot;不推荐的方法但能解决问题&quot;&gt;不推荐的方法（但能解决问题）&lt;/h2&gt;

&lt;p&gt;我在本地把 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支 &lt;code class=&quot;highlighter-rouge&quot;&gt;reset&lt;/code&gt; 到那次误提交文件的提交之前，然后跳过有问题的提交，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;origin/develop&lt;/code&gt; 分支上其它新提交一个个 &lt;code class=&quot;highlighter-rouge&quot;&gt;cherry-pick&lt;/code&gt; 到本地的 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支上。需要注意不能 &lt;code class=&quot;highlighter-rouge&quot;&gt;cherry-pick&lt;/code&gt; 那些合并的提交（就是那种有两个 parent 的提交）。&lt;/p&gt;

&lt;p&gt;这样，本地的 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支就刚好跳过那个误传文件的提交，而包含之后的所有提交了。&lt;/p&gt;

&lt;h2 id=&quot;不管哪种方法执行完之后都要做这些事情&quot;&gt;不管哪种方法，执行完之后都要做这些事情&lt;/h2&gt;

&lt;p&gt;上面两种方法执行完之后，都面临一个问题——本地 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 和远端 &lt;code class=&quot;highlighter-rouge&quot;&gt;origin/develop&lt;/code&gt; 分支将不再是快进式的，而且包含相同修改的不同提交。这是因为提交虽然还是之前那些提交，但提交的信息已经改变（SHA-1 和 parent）。&lt;/p&gt;

&lt;p&gt;我必须让远程 Git 仓库应用我的最新分支才行。必须使用一个危险的命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是去远程服务器上取消了 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支的保护，执行以上命令后重新保护它避免之后的危险操作。（&lt;strong&gt;前面不管那种方法都会面临这个危险操作&lt;/strong&gt;，说它危险，是因为此操作会删除远端服务器上 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支上的提交，如果此前的操作做得不对，可能造成严重的代码丢失的后果！）&lt;/p&gt;

&lt;p&gt;另外，根据误传文件那个提交的 SHA-1 值，我们找到了远端还存在着包含此提交的其他人的分支，我们需要删除那些分支，确保服务器上任何分支都不包含此提交。使用的是这个命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git branch &lt;span class=&quot;nt&quot;&gt;--contains&lt;/span&gt; &amp;lt;commit&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后删除上面命令找到的远端分支（当然找小伙伴确认过可以删我才敢删，不然被打死了）：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; origin &amp;lt;branch_name&amp;gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git branch &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，远端服务器上的任何分支都不存在包含误传文件的提交了。理论上新克隆的本地仓库将不再有 2000+M 的大小，实测也是如此。但已经克隆并且包含那次提交的小伙伴该怎么办？&lt;/p&gt;

&lt;p&gt;小伙伴 &lt;a href=&quot;http://blog.lindexi.com/&quot;&gt;林德熙&lt;/a&gt; 再次提供了一组命令，我和他一起简化后整理如下：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git fetch &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt;
git checkout develop
git reset origin/develop &lt;span class=&quot;nt&quot;&gt;--hard&lt;/span&gt;
git reflog expire &lt;span class=&quot;nt&quot;&gt;--expire&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;now &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;
git gc &lt;span class=&quot;nt&quot;&gt;--prune&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;now
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;命令的解释小伙伴 &lt;a href=&quot;http://blog.lindexi.com/lindexi/post/%E5%A6%82%E4%BD%95%E5%88%A0%E9%99%A4%E9%94%99%E8%AF%AF%E6%8F%90%E4%BA%A4%E7%9A%84-git-%E5%A4%A7%E6%96%87%E4%BB%B6/&quot;&gt;林德熙有详细介绍&lt;/a&gt;。大体为：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;提取远端服务器上最新的提交（这样本地仓库才会包含我修复的那些提交）&lt;/li&gt;
  &lt;li&gt;切换到 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支（避免影响到小伙伴当前的工作分支，防止丢失工作）&lt;/li&gt;
  &lt;li&gt;将本地 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支强制重置成远端的 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支（以便丢掉本地有问题的那些提交）&lt;/li&gt;
  &lt;li&gt;立刻将所有无法跟踪到的提交标记为已过期（以便垃圾回收工具可以回收）&lt;/li&gt;
  &lt;li&gt;立刻进行垃圾回收（这样误传文件的那次提交包含的 1.47GB 空间就被回收啦）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要注意：必须确保本地和远端没有任何分支可以跟踪到那次误传文件的提交。&lt;/p&gt;

&lt;p&gt;如果本地没有基于之前的 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支做新的修改，则以上命令足以将本地磁盘的空间收回。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-18-23-31-57.png&quot; alt=&quot;回收空间&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但如果本地还有新的提交，以上命令敲完前三条后就要暂停了。需要将新的提交 &lt;code class=&quot;highlighter-rouge&quot;&gt;cherry-pick&lt;/code&gt; 到新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt; 分支上；随后删除之前提交的那个分支，确保本地也没有任何分支包含误传文件的提交。随后继续敲后面的两条命令，也可以将本地的磁盘空间回收。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;至此，完结了吗？不！没有。&lt;/p&gt;

&lt;p&gt;我们肯定还有小伙伴没有删干净，等哪一天再次把那个提交推送……今天算是白干了……&lt;/p&gt;

&lt;p&gt;不过，好在我们必须经过代码审查才会将功能分支合并到 &lt;code class=&quot;highlighter-rouge&quot;&gt;develop&lt;/code&gt;。只要我们能及时发现，告诉大家在 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt; 发现花太长时间的时候立刻 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt; 取消 &lt;code class=&quot;highlighter-rouge&quot;&gt;fetch&lt;/code&gt;，那么我们还能拯救。而这次，只需要一个命令即可解决：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; origin 死灰复燃的包含误传提交的某个分支名
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;另外再吐个槽，以上一切都做完了，还写完了这篇博客。结果 &lt;code class=&quot;highlighter-rouge&quot;&gt;filter-branch&lt;/code&gt; 那个命令依然还跑着没有结束……&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Sep 2017 23:36:22 +0000</pubDate>
        <link>https://blog.walterlv.com/git/2017/09/18/delete-a-file-from-whole-git-history.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/git/2017/09/18/delete-a-file-from-whole-git-history.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>使用 PowerShell 获取 CLR 版本号</title>
        <description>&lt;p&gt;在我之前写的一篇文章&lt;a href=&quot;/dotnet/2017/09/22/dotnet-version.html&quot;&gt;.NET Framework 4.x 程序到底运行在哪个 CLR 版本之上&lt;/a&gt;中，我们说到 CLR 版本和 .NET Framework 基础库之间是有差别的，其版本号更是有差别的。不过其中并没有给出方法获取 CLR 的版本号。本文将给出几种方便的获取 CLR 版本号的方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;写代码获取&quot;&gt;写代码获取&lt;/h2&gt;

&lt;p&gt;.NET Framework 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Environment&lt;/code&gt; 类型的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt; 属性直接可以获取到版本号。于是只需要简单写一个控制台程序即可获取。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上只写关键的一行，其他类啊、&lt;code class=&quot;highlighter-rouge&quot;&gt;Main&lt;/code&gt; 函数啊、&lt;code class=&quot;highlighter-rouge&quot;&gt;Console.ReadKey&lt;/code&gt; 的都自行脑补即可。因为这不是本文重点。&lt;/p&gt;

&lt;p&gt;在我的 Windows 10 创造者更新 1703 上得到的结果是：&lt;code class=&quot;highlighter-rouge&quot;&gt;4.0.30319.42000&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;用-powershell-获取&quot;&gt;用 PowerShell 获取&lt;/h2&gt;

&lt;p&gt;考虑到 PowerShell 可以直接使用到 .NET Framework 中的类型，于是上面的代码很容易直接翻译成 PowerShell 脚本：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Major&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Minor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Revision&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;30319&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;42000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的第一行是脚本，后面全是输出，本文之后的 PowerShell 代码部分都是这样。&lt;/p&gt;

&lt;p&gt;可以看到，虽然格式不同，但依然拿到了跟我们写代码一模一样的结果。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;本文只是一个引子，你可以拿着 PowerShell 去调用其他 .NET Framework 的类和方法，根本不需要打开 Visual Studio 编译，非常方便！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不过既然是 PowerShell，那就有更多可以尝试的方法，比如说直接拿 PowerShell 的全局变量：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$PSVersionTable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                           &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PSVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;5.1.15063.608&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PSEdition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Desktop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PSCompatibleVersions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BuildVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10.0.15063.608&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CLRVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;4.0.30319.42000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WSManStackVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PSRemotingProtocolVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2.3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializationVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1.1.0.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果觉得杂乱项太多，直接取里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CLRVersion&lt;/code&gt; 即可：&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Users\lvyi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$PSVersionTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CLRVersion&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Major&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Minor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Revision&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;--------&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;30319&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;42000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 27 Sep 2017 16:24:08 +0000</pubDate>
        <link>https://blog.walterlv.com/post/powershell/2017/09/28/get-clr-version-via-powershell.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/powershell/2017/09/28/get-clr-version-via-powershell.html</guid>
        
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>试图在 Windows 10 上安装 .NET Framework 3.5 时提示错误 0x800F081F</title>
        <description>&lt;p&gt;说到在 Windows 10 上安装 .NET Framework 3.5，想必已经没什么可以多说的了，直接去“启用或关闭 Windows 功能”界面给“.NET Framework 3.5”打个勾就好了。&lt;/p&gt;

&lt;p&gt;但今天帮助一位朋友安装时却在上述步骤之后出现了错误：0x800F081F。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;正常安装步骤：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-22-23-53-09.png&quot; alt=&quot;在 Windows 10 上安装 .NET Framework 3.5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-22-23-55-50.png&quot; alt=&quot;错误代码：0x800F081F&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这不能忍啊！迅速在网上搜索错误码，然而得到的回答要么是换命令行，要么是重置系统。命令行的方案以前其实尝试过，需要一个本地的安装镜像，然而突然间哪里那么方便找一个安装镜像呢！重置系统损失太大还是算了。&lt;/p&gt;

&lt;p&gt;虽然命令行我们不用，但还是贴出来参考：&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:E:&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;ources&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;xs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中，E 盘是系统镜像盘。命令不区分大小写。&lt;/p&gt;

&lt;p&gt;然而，微软官方其实对此问题是有说明的，在 &lt;a href=&quot;https://support.microsoft.com/en-us/help/2734782/net-framework-3-5-installation-error-0x800f0906--0x800f081f--0x800f09&quot;&gt;.NET Framework 3.5 installation error: 0x800F0906, 0x800F081F, 0x800F0907&lt;/a&gt; 里。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The source files could not be found.&lt;br /&gt;
Use the “Source” option to specify the location of the files that are required to restore the feature. For more information on specifying a source location, see http://go.microsoft.com/fwlink/?LinkId=243077.&lt;br /&gt;
The DISM log file can be found at C:\Windows\Logs\DISM\dism.log&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，此错误吗意味着文件没有找到。然而我们选择的安装方式是在线安装，找不到文件意味着根本没有下载下来。再回过头来看看之前安装的最后一个步骤，文案是“从 Windows 更新下载文件”。莫非是 Windows 更新的锅？&lt;/p&gt;

&lt;p&gt;虽说是我帮助我的朋友安装，但他非常聪明，根据此猜测直接从 Cortana 搜索“服务”进入到服务管理界面，将“Windows Update”服务的启动方式从禁用改为手动。于是再重复本文一开始的步骤，成功啦！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;总结：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;0x800F081F 错误代表用于安装的文件缺失&lt;/li&gt;
  &lt;li&gt;如果是在线安装，则需要修复 Windows Update 服务&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://answers.microsoft.com/en-us/insider/forum/insider_wintp-insider_install/how-to-instal-net-framework-35-on-windows-10/450b3ba6-4d19-45ae-840e-78519f36d7a4&quot;&gt;How to instal .NET Framework 3.5 on Windows 10 - Microsoft Community&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.microsoft.com/en-us/help/2734782/net-framework-3-5-installation-error-0x800f0906--0x800f081f--0x800f09&quot;&gt;.NET Framework 3.5 installation error: 0x800F0906, 0x800F081F, 0x800F0907&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tenforums.com/software-apps/16594-net-3-5-framework-cannot-install-0x800f081f.html&quot;&gt;Net 3.5 framework, cannot install 0x800F081F. Solved - Windows 10 Forums&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 22 Sep 2017 16:15:46 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2017/09/23/install-dotnet35-on-windows-10.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2017/09/23/install-dotnet35-on-windows-10.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>设置 .NET Native 运行时指令以支持反射（尤其适用于 UWP）</title>
        <description>&lt;p&gt;我们经常会尝试用一用反射来解决一部分动态可执行代码的问题，不过这个问题在 UWP 中似乎并不那么轻松。也许你写了一句获取某个类所有属性的代码，结果发现 DEBUG 下跑得好好的，RELEASE 下居然拿不到！&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;尝试反射获取属性&quot;&gt;尝试反射获取属性&lt;/h2&gt;

&lt;p&gt;你的代码可能是这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者这样的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTypeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaredProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但无论哪一种，DEBUG 下 &lt;code class=&quot;highlighter-rouge&quot;&gt;properties&lt;/code&gt; 集合中有我们想要的属性集合，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-20-23-55-48.png&quot; alt=&quot;DEBUG 下拿到的属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;type.GetTypeInfo().DeclaredProperties&lt;/code&gt; 帮我们拿到了当前类的属性，&lt;code class=&quot;highlighter-rouge&quot;&gt;typeof(SomeType).GetProperties()&lt;/code&gt; 帮我们拿到了当前类和其所有父类的属性。&lt;/p&gt;

&lt;p&gt;然而，RELEASE 下的结果却是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-21-00-00-12.png&quot; alt=&quot;DEBUG 下拿到的属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中后者虽然有两个实例，却是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-21-00-02-54.png&quot; alt=&quot;空和继承的属性&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这就诡异了，DEBUG 和 RELEASE 下到底有什么区别呢？&lt;/p&gt;

&lt;h2 id=&quot;设置-net-本机工具链编译选项&quot;&gt;设置 .NET 本机工具链编译选项&lt;/h2&gt;

&lt;p&gt;经过一番 Google，发现 RELEASE 下编译开启了 .NET 本机工具链选项，这使得 RELEASE 下生成的是静态的本机代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-20-23-40-39.png&quot; alt=&quot;RELEASE 下的 .NET 本机工具链编译选项&quot; /&gt;&lt;/p&gt;

&lt;p&gt;试着去掉这个选项，果然以上的反射代码能够得到期望的属性集合。然而这样就丢失了 .NET Native 带给我们棒棒的性能优势了啊！&lt;/p&gt;

&lt;h2 id=&quot;设置-net-native-运行时指令&quot;&gt;设置 .NET Native 运行时指令&lt;/h2&gt;

&lt;p&gt;所以更推荐的做法是什么呢？微软为我们提供了设置 .NET Native 运行时指令的方法，展开解决方案项目的 Properties 文件夹，我们可以找到 &lt;code class=&quot;highlighter-rouge&quot;&gt;Default.rd.xml&lt;/code&gt; 文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-21-00-11-31.png&quot; alt=&quot;Default.rd.xml&quot; /&gt;&lt;/p&gt;

&lt;p&gt;查看里面的内容，微软为我们写了很详细的注释：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--
    此文件包含 .NET Native 使用的运行时指令。此处的默认值适合大多数
    开发人员。但可通过修改这些参数来修改 .NET Native
    优化程序的行为。

    运行时指令记录在 https://go.microsoft.com/fwlink/?LinkID=391919

    完全启用对 App1.MyClass 及其所有公共/私有成员的反射
    &amp;lt;Type Name=&quot;App1.MyClass&quot; Dynamic=&quot;Required All&quot;/&amp;gt;

    通过 System.Int32 启用 AppClass&amp;lt;T&amp;gt; 的特定实例的动态创建
    &amp;lt;TypeInstantiation Name=&quot;App1.AppClass&quot; Arguments=&quot;System.Int32&quot; Activate=&quot;Required Public&quot; /&amp;gt;

    使用 Namespace 指令将反射策略应用于特定命名空间中的所有类型
    &amp;lt;Namespace Name=&quot;DataClasses.ViewModels&quot; Serialize=&quot;All&quot; /&amp;gt;
--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到微软其实已经直接在注释里告诉了我们此文件的目的和用法了。而且此文件几乎就是设计来解决反射问题的！于是，我们把我们测试用的类放进去试试看，如下：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Directives&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/netfx/2013/01/metadata&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Name=&quot;*Application*&quot; 的程序集元素将应用到应用程序包中的所有程序集。星号不是通配符。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Assembly&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*Application*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Dynamic=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Required All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在此处添加应用程序特定的运行时指令。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Type&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ReflectionDemo.MainPage&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Dynamic=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Required All&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directives&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;结果并没有得到任何改变……&lt;/p&gt;

&lt;p&gt;但注意到微软同时还有一个注释，&lt;code class=&quot;highlighter-rouge&quot;&gt;*Application*&quot; 的程序集元素将应用到应用程序包中的所有程序集。星号不是通配符。&lt;/code&gt;这是说，我们自己项目中的所有程序包其实都是支持反射的，只是我们引用的微软库才不支持。于是将我们测试用的类 &lt;code class=&quot;highlighter-rouge&quot;&gt;MainPage&lt;/code&gt; 的基类都放进去，为了简单，我按照微软的注释写成了命名空间。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Directives&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/netfx/2013/01/metadata&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Application&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Name=&quot;*Application*&quot; 的程序集元素将应用到应用程序包中的所有程序集。星号不是通配符。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Assembly&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*Application*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Dynamic=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Required All&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 在此处添加应用程序特定的运行时指令。--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Namespace&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Windows.UI.Xaml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Serialize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Application&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directives&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;再次运行，已经可以反射获取到所有的属性了。经过尝试，在写了 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml&lt;/code&gt; 命名空间后，它的子命名空间 &lt;code class=&quot;highlighter-rouge&quot;&gt;Windows.UI.Xaml.Controls&lt;/code&gt; 是可以不用写的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35359942/type-getproperties-doesnt-work-in-release/35361710&quot;&gt;c# - Type.GetProperties() doesn’t work in Release - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/39365184/adding-runtime-directives-for-generic-types-in-uwp-app&quot;&gt;c# - Adding runtime directives for generic types in UWP app - Stack Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dotnet/2014/05/20/net-native-deep-dive-dynamic-features-in-static-code/&quot;&gt;.NET Native Deep Dive: Dynamic Features in Static Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 20 Sep 2017 23:35:41 +0000</pubDate>
        <link>https://blog.walterlv.com/uwp/2017/09/21/reflection-using-dotnet-native-runtime-directive.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/uwp/2017/09/21/reflection-using-dotnet-native-runtime-directive.html</guid>
        
        
        <category>uwp</category>
        
      </item>
    
      <item>
        <title>使用 filter-branch 从 Git 历史中删除一个文件</title>
        <description>&lt;p&gt;昨天帮助小伙伴&lt;a href=&quot;/git/2017/09/18/delete-a-file-from-whole-git-history.html&quot;&gt;从 Git 提交历史中删除了一个文件&lt;/a&gt;，虽然一开始尝试使用 filter-branch，但是因为需要的时间太久，就放弃了，转而使用 cherry-pick 的方案。&lt;/p&gt;

&lt;p&gt;但是，毕竟 Git 官方给的方案是 filter-branch，所以今天就在另一位小伙伴的帮助下好好阅读 Git 官方文档：&lt;a href=&quot;https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2&quot;&gt;Git - 重写历史&lt;/a&gt; 和 &lt;a href=&quot;https://git-scm.com/docs/git-filter-branch&quot;&gt;Git - git-filter-branch Documentation&lt;/a&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;原来参数中是可以指定查找范围的！几万个提交的仓库，既然是当天加的文件，当然没必要遍历整个仓库，数了数大概是 20+ 个提交之前加的，于是写出了如下命令：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--tree-filter&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'git rm file-name-to-remove.zip'&lt;/span&gt; HEAD~30..HEAD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后那个参数就是从当前分支的前 30 次提交开始遍历，这样就会快很多。&lt;/p&gt;

&lt;p&gt;下面的输入和输出是我在拿上一篇博客文件做试验，删掉那篇博客。整个仓库有 125 次提交，但只需遍历参数里指定的 5 个。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--tree-filter&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rm _posts/2017-09-18-delete-a-file-from-whole-git-history.md'&lt;/span&gt; HEAD~5..HEAD
Rewrite f74ff6c8057dcfdf96822989a09c357ae07cd2f8 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;5/5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2 seconds passed, remaining 0 predicted&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Ref &lt;span class=&quot;s1&quot;&gt;'refs/heads/master'&lt;/span&gt; was rewritten
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2&quot;&gt;Git - 重写历史&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-filter-branch&quot;&gt;Git - git-filter-branch Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.link-intersystems.com/blog/2014/07/17/remove-directories-and-files-permanently-from-git/&quot;&gt;Link Intersystems – Remove directories and files permanently from git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Sep 2017 12:19:04 +0000</pubDate>
        <link>https://blog.walterlv.com/git/2017/09/19/delete-file-using-filter-branch.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/git/2017/09/19/delete-file-using-filter-branch.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>Why Unload Twice</title>
        <description>&lt;p&gt;Sometimes WPF raise unload event twice. In this case, it happens when a logical tree is build all by myself. Why unload twice? It really confused me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Any help would be appreciated!&lt;/strong&gt; The repo: &lt;a href=&quot;https://github.com/walterlv/why-unload-twice&quot;&gt;walterlv/why-unload-twice @ GitHub&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Why the &lt;code class=&quot;highlighter-rouge&quot;&gt;_problemChild&lt;/code&gt; unloaded twice?&lt;/p&gt;

&lt;h2 id=&quot;how-to-reproduce-it-&quot;&gt;How to reproduce it ?&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Clone this repository;&lt;/li&gt;
  &lt;li&gt;Run/Debug;&lt;/li&gt;
  &lt;li&gt;Click &lt;em&gt;Add Child&lt;/em&gt; button several times;&lt;/li&gt;
  &lt;li&gt;Every time you click &lt;em&gt;Delete Child&lt;/em&gt;, you’ll got &lt;em&gt;Content Unloaded&lt;/em&gt; output twice.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;key-points&quot;&gt;Key points&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A non-generic style is added.&lt;/li&gt;
  &lt;li&gt;When removing an FrameworkElement from a visual tree, it is also been removed from a logical tree at the same time.&lt;/li&gt;
  &lt;li&gt;The visual tree and the logical tree is different.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;key-pointszh-chs---复现此问题的关键&quot;&gt;Key points(zh-CHS) - 复现此问题的关键&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;为一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;TemplatedControl&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;CustomControl&lt;/code&gt;） 定制非默认模板（不放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Generic.xaml&lt;/code&gt; 中的模板）；&lt;/li&gt;
  &lt;li&gt;这个控件包含视觉子级和逻辑子级，并且这两个不是同一个对象；&lt;/li&gt;
  &lt;li&gt;将这个控件同时从视觉树和逻辑树中移除（两个移除顺序不重要，但一定是无中断的）；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这时你会观察到从这个控件的逻辑子级开始向所有子节点递归引发 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件，然后又从这个控件本身开始向所有子节点递归引发 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件，于是所有子节点都会连续发生两次 &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件。&lt;/p&gt;

&lt;h4 id=&quot;key-pointszh-cht---復現此問題的關鍵&quot;&gt;Key points(zh-CHT) - 復現此問題的關鍵&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;為一個 &lt;code class=&quot;highlighter-rouge&quot;&gt;TemplatedControl&lt;/code&gt;（&lt;code class=&quot;highlighter-rouge&quot;&gt;CustomControl&lt;/code&gt;） 定制非默認模板（不放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Generic.xaml&lt;/code&gt; 中的模板）；&lt;/li&gt;
  &lt;li&gt;這個控件包含視覺子級和邏輯子級，並且這兩個不是同一個對象；&lt;/li&gt;
  &lt;li&gt;將這個控件同時從視覺樹和邏輯樹中移除（兩個移除順序不重要，但一定是無中斷的）；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;這時你會觀察到從這個控件的邏輯子級開始向所有子節點遞歸引發&lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件，然後又從這個控件本身開始向所有子節點遞歸引發&lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件，於是所有子節點都會連續發生兩次&lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; 事件。&lt;/p&gt;

&lt;h2 id=&quot;stacks-of-this-issue&quot;&gt;Stacks of this issue&lt;/h2&gt;

&lt;h3 id=&quot;when-a-non-generic-style-of-child-is-added-the-unloaded-event-will-be-raised-of-these-two-operations&quot;&gt;When a non-generic style of &lt;code class=&quot;highlighter-rouge&quot;&gt;Child&lt;/code&gt; is added, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; event will be raised of these two operations.&lt;/h3&gt;

&lt;p&gt;Removing from visual tree with non-generic style&lt;br /&gt;
&lt;img src=&quot;https://github.com/walterlv/why-unload-twice/raw/master/Docs/removing-from-visual-tree-with-non-generic-style.png&quot; alt=&quot;Removing from visual tree with non-generic style&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Removing from logical tree with non-generic style&lt;br /&gt;
&lt;img src=&quot;https://github.com/walterlv/why-unload-twice/raw/master/Docs/removing-from-logical-tree-with-non-generic-style.png&quot; alt=&quot;Removing from logical tree with non-generic style&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;when-only-the-generic-style-of-child-is-defined-the-unloaded-event-will-be-raised-of-only-one-operations&quot;&gt;When only the generic style of &lt;code class=&quot;highlighter-rouge&quot;&gt;Child&lt;/code&gt; is defined, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Unloaded&lt;/code&gt; event will be raised of only one operations.&lt;/h3&gt;

&lt;p&gt;Removing from visual tree with generic style&lt;br /&gt;
&lt;img src=&quot;https://github.com/walterlv/why-unload-twice/raw/master/Docs/removing-from-visual-tree-with-generic-style.png&quot; alt=&quot;Removing from visual tree with generic style&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Sep 2017 10:32:39 +0000</pubDate>
        <link>https://blog.walterlv.com/wpf/2017/09/19/why-unload-twice.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/wpf/2017/09/19/why-unload-twice.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>找回你 C 盘丢失的空间（SpaceSniffer）</title>
        <description>&lt;p&gt;什么鬼！C 盘空间满了！我分了 120GB 啊！！！是不是要删软件删游戏，是不是要重装系统？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-14-36-58.png&quot; alt=&quot;C 盘空间已满&quot; /&gt;&lt;/p&gt;

&lt;p&gt;尤其是程序员，那么多开发环境（Visual Studio 不说话 :)）空间占用那叫一个大啊！为了避免重装系统，我找到了一款神奇的软件——SpaceSniffer。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;话不多说，上神器：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;SpaceSniffer 官网：&lt;a href=&quot;http://www.uderzo.it/main_products/space_sniffer/index.html&quot;&gt;Uderzo Software SpaceSniffer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;SpaceSniffer 官方下载镜像：&lt;a href=&quot;https://www.fosshub.com/SpaceSniffer.html&quot;&gt;Download SpaceSniffer Portable&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下载完后解压：&lt;br /&gt;
&lt;img src=&quot;/static/posts/2017-09-17-14-49-11.png&quot; alt=&quot;Space Sniffer 文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于是绿色版，下载解压后直接运行即可。需要注意的是，为了能够发现系统文件夹中的大文件元凶，建议使用管理员权限运行。&lt;/p&gt;

&lt;p&gt;启动后会弹出磁盘选择对话框，由于我 C 盘满了，所以我选择了 C。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-14-50-33.png&quot; alt=&quot;选择 C 盘&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后就看着它又炫酷又丑的界面慢慢等待分析吧！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-space-sniffer.gif&quot; alt=&quot;正在分析&quot; /&gt;&lt;/p&gt;

&lt;p&gt;等分析的速度放缓，则可以认为它分析完了。因为它还会随时监视文件的修改，所以分析是永远不会结束的，不用等了。我这边分析完之后，发现 AppData 目录居然占了 34.5 GB！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-01-35.png&quot; alt=&quot;分析结果&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一层层点开，发现 Photoshop 临时文件夹占了我 26.6GB！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-02-43.png&quot; alt=&quot;Photoshop 是大头&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是这才发现我还开着 Photoshop，关掉它，C 盘空间恢复。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-04-42.png&quot; alt=&quot;C 盘空间恢复（计算机）&quot; /&gt;
&lt;img src=&quot;/static/posts/2017-09-17-15-04-04.png&quot; alt=&quot;C 盘空间恢复（Space Sniffer）&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果空间依然不够，继续一层层点开最大的的文件夹，把有问题的干掉：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;如果是临时文件，可以关掉程序或直接删掉整个文件夹&lt;/li&gt;
  &lt;li&gt;如果是程序文件，则可以考虑只卸载这个程序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;比如我还发现，原来 ReSharper 会占用这么大的空间……&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2017-09-17-15-06-43.png&quot; alt=&quot;ReSharper&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;最后多说一句，不要吐槽为何我的 C 盘只有 120GB，因为我用的是 Surface pro 啊，总共就 256GB 可用。考虑到系统出问题可以随时重装，重要资料当然不会放到 C 盘。&lt;/p&gt;
</description>
        <pubDate>Sun, 17 Sep 2017 07:13:33 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2017/09/17/find-lost-space-using-space-sniffer.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2017/09/17/find-lost-space-using-space-sniffer.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>如何向整个 Git 仓库补提交一个文件</title>
        <description>&lt;p&gt;微软在 &lt;a href=&quot;https://referencesource.microsoft.com/&quot;&gt;Reference Source&lt;/a&gt; 里开放了 .NET Framework 多个版本的源码。为了更方便地阅读这些源码，我们把每一个版本都下载下来后按顺序提交到 git 仓库中。&lt;/p&gt;

&lt;p&gt;但是！！！居然忘了在第一次提交之前放一个 .gitignore 文件！如果没有这个文件，那我们每次打开源码查看都会带来一大堆不明所以的修改文件。那么多的源码，绝对不会想重新挨个版本再提交一次。于是找到了一条可以解决这个问题的 git 命令。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--index-filter&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cp /C/仓库外面某个路径下的/.gitignore . &amp;amp;&amp;amp; git add .gitignore&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行之后，&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\仓库外面某个路径下的\.gitignore&lt;/code&gt; 文件就被添加到了当前分支的第一次提交里面，并且查看后面任何一次提交对应的全部文件时，都会有这个文件。&lt;/p&gt;

&lt;p&gt;如果希望此操作对所有分支生效，则加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;--all&lt;/code&gt; 参数，即：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git filter-branch &lt;span class=&quot;nt&quot;&gt;--index-filter&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cp /C/仓库外面某个路径下的/.gitignore . &amp;amp;&amp;amp; git add .gitignore&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;写这个命令时需要注意：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;git 在 Bash 里写的时候，&lt;code class=&quot;highlighter-rouge&quot;&gt;C:\&lt;/code&gt; 需要写成 &lt;code class=&quot;highlighter-rouge&quot;&gt;/C/&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;-- --all&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;--&lt;/code&gt; 是用来区分路径和提交的，官方说法是：&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fatal: ambiguous argument &lt;span class=&quot;s1&quot;&gt;'cp /C/仓库外面某个路径下的/.gitignore . &amp;amp;&amp;amp; git add .g
itignore'&lt;/span&gt;: unknown revision or path not &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the working tree.
Use &lt;span class=&quot;s1&quot;&gt;'--'&lt;/span&gt; to separate paths from revisions, like this:
&lt;span class=&quot;s1&quot;&gt;'git &amp;lt;command&amp;gt; [&amp;lt;revision&amp;gt;...] -- [&amp;lt;file&amp;gt;...]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行了此命令后，所有的提交其实都被重写了，提交号已经改变。如果你从未推送到远端过，那么恭喜，你已经在神不知鬼不觉中添加了一个 .gitignore 文件，就像是第一次提交就加了这个文件一样。&lt;/p&gt;

&lt;p&gt;但是，如果此前有推送到过远程分支，请慎重！因为你的此次推送的命令和其他人首次拉取的命令将有所改变：&lt;/p&gt;

&lt;p&gt;推送（所有分支，强制）：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;拉取（获取 + 重置）&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git fetch
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git reset origin/master &lt;span class=&quot;nt&quot;&gt;--hard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/21353584/git-how-do-i-add-a-file-to-the-first-commit-and-rewrite-history-in-the-process&quot;&gt;GIT: How do I add a file to the first commit (and rewrite history in the process)?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 13 Sep 2017 11:10:47 +0000</pubDate>
        <link>https://blog.walterlv.com/git/2017/09/13/add-file-to-whole-git-repository.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/git/2017/09/13/add-file-to-whole-git-repository.html</guid>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>Exception.Data 为异常添加更多调试信息</title>
        <description>&lt;p&gt;我们抛出异常是为了知道程序中目前的状态发生了错误。为了能够知道错误的详细信息便于我们将来避免产生这样的错误，我们会选用合适的异常类型，在异常中编写易于理解的 message 信息。但是有时我们需要更多的信息进行调试才能帮忙在将来避免这个异常。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;System.Exception&lt;/code&gt; 类中就自带了这样的属性 &lt;code class=&quot;highlighter-rouge&quot;&gt;Data&lt;/code&gt;，它是 &lt;code class=&quot;highlighter-rouge&quot;&gt;IDictionary&lt;/code&gt; 类型的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecuritySafeCritical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// auto-generated&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsImmutableAgileException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmptyReadOnlyDictionaryInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ListDictionaryInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;别问我为什么把括号放最右边，那是微软自己写的源码&lt;/em&gt; &lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/exception.cs,150&quot;&gt;点击这里查看&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最近在调试 .NET Framework 内部代码的异常时就发现微软就是使用这个属性储存异常的更多细节的：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterStylusDeviceCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__stylusDeviceLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusDeviceId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// The map must contain unique entries for each stylus device.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__stylusDeviceMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stylusDeviceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;InvalidOperationException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ioe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We add a tag here so we can check for this specific exception&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// in TabletCollection when adding new tablet devices.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ioe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;System.Windows.Input.StylusLogic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ioe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;__stylusDeviceMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stylusDeviceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;以上代码出自 .NET Framework 4.6 的&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;System.Windows.Input.StylusLogic&lt;/code&gt; &lt;em&gt;类型，https://referencesource.microsoft.com 里 .NET Framework 4.7 中找不到。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;需要注意的是，&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; 方法并不会把这个字典转成字符串的任意一个部分；所以，如果需要在日志中记录程序中全局捕获的异常，需要自己去遍历异常中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Data&lt;/code&gt; 的每一项。不过，为了解决掉更多的程序错误，我们记录日志的时候不已经写了更多的信息（比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;InnerException&lt;/code&gt;）了吗？&lt;/p&gt;
</description>
        <pubDate>Tue, 12 Sep 2017 07:44:36 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2017/09/12/exception-data.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2017/09/12/exception-data.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 渲染系统（WPF Render System）</title>
        <description>&lt;p&gt;一个朋友问我：“为什么几千个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 在视觉树上，增加删除几个能够那么快地渲染出来？”这个问题问倒我了，因为我对 WPF 渲染系统的了解很少，更不知道渲染部分和 UI 逻辑部分是如何分工的。&lt;br /&gt;
在此机会下，我毫不犹豫地打开 &lt;a href=&quot;https://referencesource.microsoft.com/&quot;&gt;https://referencesource.microsoft.com/&lt;/a&gt; 阅读 WPF 的源码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;对&lt;strong&gt;探索源码&lt;/strong&gt;章节不感兴趣的读者，可以直接跳到后面&lt;strong&gt;大胆猜想&lt;/strong&gt;与&lt;strong&gt;理论依据&lt;/strong&gt;章节。&lt;/p&gt;

&lt;h2 id=&quot;探索源码&quot;&gt;探索源码&lt;/h2&gt;

&lt;p&gt;虽然知道 &lt;a href=&quot;https://referencesource.microsoft.com/&quot;&gt;referencesource&lt;/a&gt; 上有源码，但从哪个类哪个方法开始也是个问题。&lt;/p&gt;

&lt;h4 id=&quot;寻找入口&quot;&gt;寻找入口&lt;/h4&gt;

&lt;p&gt;既然是添加、移除视觉树节点，那么应该在这几个类中找得到相关方法：&lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTreeHelper&lt;/code&gt;、&lt;code class=&quot;highlighter-rouge&quot;&gt;VisualCollection&lt;/code&gt;。试着找了下 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualTreeHelper&lt;/code&gt;，结果只有各种 &lt;code class=&quot;highlighter-rouge&quot;&gt;Get&lt;/code&gt; 方法；找了下 &lt;code class=&quot;highlighter-rouge&quot;&gt;VisualCollection&lt;/code&gt;，结果找到了。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;public int Add(Visual visual)
{
    // …… （省略部分）
    if (visual != null)
    {
        ConnectChild(addedPosition, visual);
    }
    // …… （省略部分）
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;依此为突破口，应该能一层层找到渲染 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 部分的代码吧！&lt;/p&gt;

&lt;h4 id=&quot;层层进入&quot;&gt;层层进入&lt;/h4&gt;

&lt;p&gt;我们看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;ConnectChild&lt;/code&gt; 方法，随后一层层进入。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;private void ConnectChild(int index, Visual value)
{
    // …… （省略部分）
    _owner.InternalAddVisualChild(value);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中，&lt;code class=&quot;highlighter-rouge&quot;&gt;_owner&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 类型。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;internal void InternalAddVisualChild(Visual child)
{
    this.AddVisualChild(child);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来就不那么顺利了，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddVisualChild&lt;/code&gt; 方法进去后发现仅设置了标志位，再没有执行实质性的方法。这下杯具了，此路不通。但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddVisualChild&lt;/code&gt; 方法旁边还有个 &lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveVisualChild&lt;/code&gt; 方法，顺手看了下，居然有实质性方法：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;protected void RemoveVisualChild(Visual child)
{
    // …… （省略部分）
    for (int i = 0; i &amp;lt; _proxy.Count; i++)
    {
        DUCE.Channel channel = _proxy.GetChannel(i);

        if (child.CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent))
        {
            child.SetFlags(channel, false, VisualProxyFlags.IsConnectedToParent);
            DUCE.IResource childResource = (DUCE.IResource)child;
            childResource.RemoveChildFromParent(this, channel);
            childResource.ReleaseOnChannel(channel);
        }
    }
    // …… （省略部分）
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意到 &lt;code class=&quot;highlighter-rouge&quot;&gt;childResource.RemoveChildFromParent(this, channel);&lt;/code&gt; 的调用方是 &lt;code class=&quot;highlighter-rouge&quot;&gt;childResource&lt;/code&gt;，而它是从 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 强转的 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.IResource&lt;/code&gt; 接口对象。由于我们要了解的是实现细节，直接点开接口是看不到的，所以，得看看到底是谁实现了这个接口。既然是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 强转得到，那么确定是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual&lt;/code&gt; 实现。但必须要强转才能调用，这不得不让我怀疑“Visual 类&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms173157.aspx&quot;&gt;显式实现&lt;/a&gt;了接口 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.IResource&lt;/code&gt;”。  &lt;br /&gt;
于是，我在浏览器中按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl&lt;/code&gt;+&lt;code class=&quot;highlighter-rouge&quot;&gt;F&lt;/code&gt;，搜素 &lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveChildFromParent&lt;/code&gt;，结果不出所料：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;/// &amp;lt;summary&amp;gt;
/// Sends a command to compositor to remove the child
/// from its parent on the channel.
/// &amp;lt;/summary&amp;gt;
void DUCE.IResource.RemoveChildFromParent(
        DUCE.IResource parent,
        DUCE.Channel channel)
{
    DUCE.CompositionNode.RemoveChild(
        parent.GetHandle(channel),
        _proxy.GetHandle(channel),
        channel);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其实我们可以继续去看 &lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveChild&lt;/code&gt; 方法，但注释却让我感到意外。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sends a command to compositor to remove the child from its parent on the channel.
在 channel 中向 compositor 发送一个移除视觉子级的命令。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;莫非到头来都不会真实地执行任何渲染相关的方法？channel 是什么？compositor 又是什么？一个一个调查！&lt;/p&gt;

&lt;h4 id=&quot;查到最后&quot;&gt;查到最后&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RemoveChild&lt;/code&gt; 方法如下，果不其然，真的只是在通道中发送了一条移除视觉子级的命令。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;/// &amp;lt;SecurityNote&amp;gt;
///     Critical: This code accesses an unsafe code block
///     TreatAsSafe: Operation is ok to call. It does not return any pointers and sending a pointer to a channel is safe
/// &amp;lt;/SecurityNote&amp;gt;
[SecurityCritical, SecurityTreatAsSafe]
internal static void RemoveChild(
    DUCE.ResourceHandle hCompositionNode,
    DUCE.ResourceHandle hChild,
    Channel channel)
{
    DUCE.MILCMD_VISUAL_REMOVECHILD command;

    command.Type = MILCMD.MilCmdVisualRemoveChild;
    command.Handle = hCompositionNode;
    command.hChild = hChild;

    unsafe
    {
        channel.SendCommand(
            (byte*)&amp;amp;command,
            sizeof(DUCE.MILCMD_VISUAL_REMOVECHILD)
            );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;channel 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.Channel&lt;/code&gt; 类型的，在 msdn 上搜索 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.Channel&lt;/code&gt; 没有得到正式的定义，但是搜到的一篇文章却间接地描述了它的用途。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/dsui_team/2013/11/18/wpf-render-thread-failures/&quot;&gt;WPF Render Thread Failures&lt;/a&gt;
The render thread only synchronizes with the UI thread in a few locations, so the callstacks above are typically where you &lt;em&gt;notice the problem, not where it actually occurred&lt;/em&gt;. The most common locations when they synchronize are when a window’s settings are updated (size, position, etc.) or as a result of the UI thread handling a “channel” message from DirectX.&lt;br /&gt;
渲染线程只在少数几处与 UI 线程进行同步，这也是为什么你看到的堆栈信息是这么几处，而不是真实发生错误的代码。通常进行线程同步的几个地方是 window 的尺寸、位置等发生变换时或者 UI 线程从 DirectX 处理通道中的消息时。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;从原文中的错误堆栈中和上文里面我们可以知道 channel 是用来让 UI 线程和渲染线程进行通信的通道。&lt;/p&gt;

&lt;p&gt;记得刚开始 &lt;code class=&quot;highlighter-rouge&quot;&gt;AddVisualChild&lt;/code&gt; 方法中没有找到相关方法吗？想知道为什么。于是我又通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.Channel&lt;/code&gt; 反向查找，最终发现对应的方法其实在 &lt;code class=&quot;highlighter-rouge&quot;&gt;Visual.Render&lt;/code&gt; 方法中。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;internal void Render(RenderContext ctx, UInt32 childIndex)
{
    // …… （省略部分）
    DUCE.CompositionNode.InsertChildAt(
        ctx.Root,
        _proxy.GetHandle(channel),
        childIndex,
        channel);
    // …… （省略部分）
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;大胆猜想&quot;&gt;大胆猜想&lt;/h2&gt;

&lt;p&gt;继续找，发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.Channel&lt;/code&gt; 类型中只有发送/提交命令的方法，没有获取命令的方法；而且发送命令执行的都是非托管代码。而如果获取命令的方法也是托管代码，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;DUCE.Channel&lt;/code&gt; 中一定有方法获取到命令的。所以，大胆猜测，获取方法仅在非托管代码中实现。也就是说，UI 线程由托管代码实现，但视觉树改变后仅发送一个命令通知渲染线程实现，而渲染线程由非托管代码实现。&lt;/p&gt;

&lt;h2 id=&quot;理论依据&quot;&gt;理论依据&lt;/h2&gt;

&lt;p&gt;Google 搜索“WPF Render”关键字，我找到了这篇文章：
&lt;a href=&quot;https://jeremiahmorrill.wordpress.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/&quot;&gt;A Critical Deep Dive into the WPF Rendering System&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从这篇文章中，得到了很多 WPF 渲染系统的启发，这也许能解释本文开始的一部分现象。&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Jan 2017 06:50:00 +0000</pubDate>
        <link>https://blog.walterlv.com/wpf/2017/01/16/wpf-render-system.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/wpf/2017/01/16/wpf-render-system.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>解决XAML设计器中遇到的那些错误</title>
        <description>&lt;p&gt;使用 Visual Studio 开发 WPF 程序时，XAML 设计器能够极大提高我们的开发效率。不止是写出的代码无需运行就能看到效果，还有能够直接在设计器中点击定位元素以及拖拽改变属性。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;但是，XAML 开发不总是那么幸运！
因为我们总能在设计器中看到这样的一幕：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2016-07-31-exception-in-xaml-designer.png&quot; alt=&quot;XAML 设计器错误&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你看完之后无奈又无助，那这篇文章正好将帮你分析 XAML 设计器出错的各种可能原因以及解决这些问题的思路。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;xaml-设计器出错的种类&quot;&gt;XAML 设计器出错的种类&lt;/h2&gt;
&lt;p&gt;总结起来，XAML 设计器错误有这些种类：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;加载错误；
    &lt;blockquote&gt;
      &lt;p&gt;你会在设计器中看到各种各样的异常。&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;设计时类型不匹配；
    &lt;blockquote&gt;
      &lt;p&gt;你会发现设计器异常提示无法将某种类型转换为另一种类型。&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;资源未找到；
    &lt;blockquote&gt;
      &lt;p&gt;你会发现设计器里的控件样式变成了默认。&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;控件尺寸难以接受或相互重叠；
    &lt;blockquote&gt;
      &lt;p&gt;有时你可能不得不将控件的尺寸设为 0；或者将多个控件放一起以至于重叠，看不见最底层的那个。&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;控件缺少用以显示的数据。
    &lt;blockquote&gt;
      &lt;p&gt;这种情况多见于绑定 ViewModel，尤其是列表类控件。&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;严格来说，只有前两种算是错误；但后三种也是让我们为设计器抓狂的罪魁祸首，所以也一并说了吧。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;解决-xaml-设计器错误的一般思路&quot;&gt;解决 XAML 设计器错误的一般思路&lt;/h2&gt;
&lt;p&gt;未完待续……&lt;/p&gt;

&lt;h3 id=&quot;阅读并猜测异常发生的来源&quot;&gt;阅读并猜测异常发生的来源&lt;/h3&gt;
&lt;p&gt;未完待续……&lt;/p&gt;

&lt;h3 id=&quot;用-vs-调试-vs&quot;&gt;用 VS 调试 VS&lt;/h3&gt;
&lt;p&gt;未完待续……&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;实战解决几个典型的-xaml-设计器错误&quot;&gt;实战解决几个典型的 XAML 设计器错误&lt;/h2&gt;
&lt;p&gt;未完待续……&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/jj871742.aspx&quot;&gt;MSDN - Troubleshooting Designer Load Failures&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ee856616.aspx&quot;&gt;MSDN - How to: Debug a Designer Load Failure&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Aug 2016 06:33:38 +0000</pubDate>
        <link>https://blog.walterlv.com/wpf/2016/07/31/solve-xaml-designer-errors.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/wpf/2016/07/31/solve-xaml-designer-errors.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>使用链接共享 Visual Studio 中的代码文件</title>
        <description>&lt;p&gt;如果你还在通过复制来共享代码就太 out 了！&lt;/p&gt;

&lt;p&gt;我们知道，将公共的代码抽取成库是非常好的代码复用手段，但有时我们需要复用的代码（或文件）无法提取到库中。这时，Visual Studio 自带的 Link 功能就派上用场了。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;链接的文件长这样&quot;&gt;链接的文件长这样&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2016-08-01-share-file-as-link.png&quot; alt=&quot;链接文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上图，链接文件其图标上会有一个小图标&lt;img src=&quot;/static/posts/2016-08-01-link-icon.png&quot; alt=&quot;图标&quot; /&gt;。图中的 A 和 B 两个项目都是应用程序项目（不是类库），所需的图标和数据库是需要共享的，于是将其设为了链接。&lt;/p&gt;

&lt;h2 id=&quot;怎么创建链接文件&quot;&gt;怎么创建链接文件？&lt;/h2&gt;

&lt;h3 id=&quot;普通的方法适用于-visual-studio-20192017201520132012&quot;&gt;普通的方法（适用于 Visual Studio 2019/2017/2015/2013/2012）&lt;/h3&gt;

&lt;p&gt;在 Visual Studio 项目上或文件夹上 &lt;code class=&quot;highlighter-rouge&quot;&gt;右键&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Add&lt;/code&gt;-&amp;gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ExistingItem&lt;/code&gt;，选好文件后，不要直接点右下角的 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Add]&lt;/code&gt; 按钮，而是点击 &lt;code class=&quot;highlighter-rouge&quot;&gt;[Add]&lt;/code&gt; 按钮旁边的下拉按钮，选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Add As Link&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;后文即将说的快速方法不适用于 Visual Studio 2019（其实是不适用于 SDK 风格的项目文件），这个 bug 我已经报给微软了，请参见：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/idea/961545/link-a-file-using-alt-dragging-is-lost-in-the-new.html&quot;&gt;Link a file using Alt-Dragging is lost in the new SDK style project. - Developer Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 SDK 风格的项目文件，可阅读：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.walterlv.com/post/introduce-new-style-csproj-into-net-framework.html&quot;&gt;将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;最快的方法适用于-visual-studio-2017201520132012&quot;&gt;最快的方法（适用于 Visual Studio 2017/2015/2013/2012）&lt;/h3&gt;

&lt;p&gt;按住 Alt 键，在 Visual Studio 中将一个文件拖拽到另一个文件夹中即可完成文件的链接。&lt;/p&gt;

&lt;p&gt;如果你想同时拖拽多个文件，你需要先拖拽，再在拖拽的过程中按住 Alt 键，否则只能拖一个文件出来（不知 Visual Studio 2015/2017 为什么要这么设计）。&lt;/p&gt;

&lt;h2 id=&quot;这个过程发生了什么&quot;&gt;这个过程发生了什么？&lt;/h2&gt;

&lt;p&gt;我们观察下包含链接的项目文件：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这是普通的文件 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Log.config&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 这是链接的文件 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\Project B\icon.ico&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Link&amp;gt;&lt;/span&gt;icon.ico&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Link&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/None&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以发现，Include 实际上是指向另一个项目中的文件，这样，msbuild 在编译的时候直接读取 include 的文件路径即可实现编译，都不需要特殊考虑。&lt;/p&gt;

&lt;p&gt;在标签内部，有一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;Link&lt;/code&gt; 标签，告诉 Visual Studio 在此项目中应该显示到哪个文件夹下，是什么文件名。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/apps/jj714082(v=vs.105).aspx&quot;&gt;MSDN - Share code with Add as Link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 01 Aug 2016 03:04:42 +0000</pubDate>
        <link>https://blog.walterlv.com/visualstudio/2016/08/01/share-code-with-add-as-link.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/visualstudio/2016/08/01/share-code-with-add-as-link.html</guid>
        
        
        <category>visualstudio</category>
        
        <category>msbuild</category>
        
      </item>
    
      <item>
        <title>KeyDown/PreviewKeyDown 事件中监听 Alt 键按下</title>
        <description>&lt;p&gt;在 WPF 应用程序（或者其他 Windows 应用程序中），为了监听 Alt 键按下，我们可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyDown&lt;/code&gt; 事件中写源码。然而，运行一看，发现并没有什么用。打个断点看下会发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;e.Key&lt;/code&gt; 的值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Key.System&lt;/code&gt;。这就奇怪了，&lt;code class=&quot;highlighter-rouge&quot;&gt;Key.System&lt;/code&gt; 是个什么鬼？&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一个坑&quot;&gt;一个坑&lt;/h2&gt;

&lt;p&gt;在WPF应用程序（或者其他 Windows 应用程序中），为了监听 Alt 键按下，我们可以尝试写出这样的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;PreviewKeyDown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LeftAlt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RightAlt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// A: 做些什么。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而，运行一看，发现并没有什么用。A处的代码根本就没执行。&lt;/p&gt;

&lt;p&gt;打个断点看下，会发现，&lt;code class=&quot;highlighter-rouge&quot;&gt;e.Key&lt;/code&gt; 的值是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Key.System&lt;/code&gt;。这就奇怪了，&lt;code class=&quot;highlighter-rouge&quot;&gt;Key.System&lt;/code&gt; 是个什么鬼？&lt;/p&gt;

&lt;h2 id=&quot;一段源码&quot;&gt;一段源码&lt;/h2&gt;

&lt;p&gt;看看 &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyEventArgs&lt;/code&gt; 中的源码，我们发现微软写了这么个注释：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     The Key referenced by the event, if the key is not being &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     handled specially.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果按键没有被特殊处理， &lt;code class=&quot;highlighter-rouge&quot;&gt;Key&lt;/code&gt; 属性才会返回正确的按键。这么说，当我们按下 Alt 键时，其实 Windows 或者 WPF 某一层特殊处理了这个按键。继续阅读源码，发现这个属性后面还有这样一个属性：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     The Key referenced by the event, if the key is going to be&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;///     processed by an system.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemKey&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_realKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;跟刚刚的 &lt;code class=&quot;highlighter-rouge&quot;&gt;Key&lt;/code&gt; 属性相反，这个属性指进行特殊处理时返回的按键。所以，截至这里，问题算是解决了，因为我们可以写出这样的代码：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;PreviewKeyDown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LeftAlt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RightAlt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// A: 做些什么。&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;一个解释&quot;&gt;一个解释&lt;/h2&gt;

&lt;p&gt;然而事情肯定不能这样就结束了，微软为什么要设计这样奇怪的机制？为什么 Alt 键要成为特殊系统按键？&lt;/p&gt;

&lt;p&gt;经过一系列搜索，我找到了解释：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;在 Windows 系统中，Alt 键会被特殊处理。当单独按下 Alt 键，或者按下一个带有 Alt 的组合键时，操作系统会将此事件视为“系统按键”（System keypress）。系统按键与普通按键相比，其处理过程是不同的。&lt;/p&gt;

  &lt;p&gt;首先，无论是系统按键还是普通按键，Windows都会将这些事件传递给应用程序。如果是普通按键，则传递 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_KEYDOWN&lt;/code&gt; ，但如果是 Alt 键，则传递 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_SYSKEYDOWN&lt;/code&gt; ；同理， &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_KEYUP&lt;/code&gt; 也会转变成 &lt;code class=&quot;highlighter-rouge&quot;&gt;WM_SYSKEYUP&lt;/code&gt;。&lt;/p&gt;

  &lt;p&gt;在 Windows 中（包括 WPF 中），Alt 键如此特殊是为了处理应用程序中那些带有“特殊标记”文字的菜单项（MenuItems）、按钮（Buttons）和其它标签（Labels）的。举个例子，如果按钮的文字内容被设置成“Say _Hi”，那么 Alt+H 快捷键就会被转变成为一次按钮点击（Click）事件。&lt;/p&gt;

  &lt;p&gt;这段解释可以在这里 &lt;a href=&quot;https://stackoverflow.com/questions/3099472/previewkeydown-is-not-seeing-alt-modifiers&quot;&gt;https://stackoverflow.com/questions/3099472/previewkeydown-is-not-seeing-alt-modifiers&lt;/a&gt; 得到更详细的信息。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Mon, 09 May 2016 02:59:00 +0000</pubDate>
        <link>https://blog.walterlv.com/wpf/2016/05/09/know-alt-is-pressed-in-key-down-event.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/wpf/2016/05/09/know-alt-is-pressed-in-key-down-event.html</guid>
        
        
        <category>wpf</category>
        
      </item>
    
      <item>
        <title>关联一个文件扩展名或协议</title>
        <description>&lt;p&gt;在 Windows 中关联程序或协议是通过注册表项实现的，编写任意一个程序甚至只是个脚本来写注册表即可实现。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;注册表项的位置在这里：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HKEY_CLASSES_ROOT\XXX\shell\open\command
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2015-07-07-file.png&quot; alt=&quot;关联到文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2015-07-07-protocol.png&quot; alt=&quot;关联到协议&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Jul 2015 12:00:00 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2015/07/07/associate-with-file-or-protocol.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2015/07/07/associate-with-file-or-protocol.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>未知的编译错误：“已添加具有相同键的项。Unknown build error, 'An item with the same key has already been added.'”</title>
        <description>&lt;p&gt;未知的编译错误：“已添加具有相同键的项。”&lt;/p&gt;

&lt;p&gt;Unknown build error, ‘An item with the same key has already been added.’&lt;/p&gt;

&lt;p&gt;本文将解释编译时产生此问题的原因，并提供解决方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;出现此问题的原因&quot;&gt;出现此问题的原因&lt;/h2&gt;

&lt;p&gt;出现此问题的原因是：csproj 文件中存在两个对相同文件的引用行。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv\Demo\Icon\Clear.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Resource&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Walterlv\Demo\Icon\Clear.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;出现此问题时，只需要去掉某一个重复行即可，如果找不到是哪个文件，则可以使用正则表达式匹配。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(?s)(&amp;lt;.+?&amp;gt;).*?\1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此正则表达式的作用是查找文件中的相同行。&lt;/p&gt;

&lt;p&gt;或者写一个简短的程序来查找：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Walterlv.Tools&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllLines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此代码的作用是输出指定文件中所有相同的行。&lt;/p&gt;

&lt;h2 id=&quot;一个让vs复现此问题的步骤&quot;&gt;一个让VS复现此问题的步骤&lt;/h2&gt;

&lt;p&gt;如下图，将一个已排除到项目之外的文件拖拽到另一个文件夹，并覆盖项目内的同名文件，则必现此问题。&lt;/p&gt;

&lt;p&gt;所以，平时开发的过程中，如果要到处拖拽文件的话，小心哦！&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;正在录制.gif&quot;&gt;正在录制&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Jun 2015 12:13:00 +0000</pubDate>
        <link>https://blog.walterlv.com/post/unkown-build-error-item-same-key-added.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/post/unkown-build-error-item-same-key-added.html</guid>
        
        
        <category>visualstudio</category>
        
      </item>
    
      <item>
        <title>为程序签名</title>
        <description>&lt;p&gt;给应用程序签名，可以让系统知道你的程序是受信任的程序。一部分操作，系统只会为受信任的程序开启。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;创建私人证书&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makecert -r -pe -n &quot;CN=Test Certificate - For Internal Use Only&quot; -ss PrivateCertStore testcert.cer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装私人证书&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;certmgr.exe -add testcert.cer -s -r localMachine root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;给程序集签名&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SignTool sign /v /s PrivateCertStore /n &quot;Test Certificate - For Internal Use Only&quot; /t http://timestamp.verisign.com/scripts/timestamp.dll APP.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 31 Mar 2015 08:46:00 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2015/03/31/sign-for-desktop-application.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2015/03/31/sign-for-desktop-application.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>减速动画 AnimationShiftKey</title>
        <description>&lt;p&gt;想不想让你 Windows 自带的各种动画都变得缓慢？这可别有一番风味啊！顺便还能观察下系统的动画是怎么实现的呢。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;修改注册表即可实现：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM]
&quot;AnimationsShiftKey&quot;=dword:00000001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 28 Dec 2014 07:57:00 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2014/12/28/decelerate-windows-animation.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2014/12/28/decelerate-windows-animation.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>为什么桌面上可能有两个同名文件？</title>
        <description>&lt;p&gt;是否见过桌面上同时存在两个同名的文件？这其实是可能的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我们见过这样的情况：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2014-09-25-two-same-files.png&quot; alt=&quot;两个 desktop.ini&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当打开隐藏文件和系统文件时，桌面上会看到这样两个同名的文件。&lt;/p&gt;

&lt;p&gt;虽然 Windows 不允许在一个文件夹中同时存在两个同名的文件，但我们知道“桌面”不是普通的文件夹。
在默认的 Windows 系统设置中，桌面上显示的图标不仅来自于当前用户帐户专有的“桌面”配置文件夹，
也来自于所有用户帐户共有的“公共桌面”配置文件夹。前者提供的图标仅在当前用户帐户的桌面上显示；
后者提供的图标在所有用户帐户的桌面上显示。由于这两个“桌面”配置文件夹都有自己的 Desktop.ini，
所以当我们允许显示隐含的文件时，两个 Desktop.ini 都将出现在桌面上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/2014-09-25-attributes.png&quot; alt=&quot;属性面板&quot; /&gt;&lt;/p&gt;

&lt;p&gt;请注意“对象名称”，其中指示两个文件存在于不同路径。&lt;/p&gt;
</description>
        <pubDate>Thu, 25 Sep 2014 10:11:00 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2014/09/25/why-two-same-file-can-be-existed-on-desktop.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2014/09/25/why-two-same-file-can-be-existed-on-desktop.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>FileSystemWatcher 监视文件改变</title>
        <description>&lt;p&gt;要监视文件或文件夹的改变，可使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;FileSystemWatcher&lt;/code&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileWatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemWatcher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watchingFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NotifyFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotifyFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NotifyFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EnableRaisingEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_fileWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileWatcher_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileWatcher_Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileSystemEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatcherChangeTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意的是，并不是按照以上这种写法就能立即收到改变通知。
有些程序写入文件是立即写入磁盘的（如记事本），在写入时能够通过以上方法立即获得通知；
但有一些不是立即写入磁盘（如 Log4Net），它会有一个缓冲区，程序写入到缓冲区时以上方法不会收到通知。&lt;/p&gt;

&lt;p&gt;然而，只要有任何一个进程读取了这个文件（哪怕是只有一个字节），Windows 就会立刻将缓冲区写入磁盘，以上方法也就会收到改变通知了。&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Sep 2014 12:22:00 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2014/09/23/FileSystemWatcher-monitor-filesystem-changed.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2014/09/23/FileSystemWatcher-monitor-filesystem-changed.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>FileShare 读取一个其它进程正在写的文件</title>
        <description>&lt;p&gt;Windows 允许多个应用程序同时读取一个文件，即便这个文件正在被写入。&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadLogLines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FileStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_watchingFileNameDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileShare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StreamReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GBK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringSplitOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveEmptyEntries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;关键在于不单要与只读方式打开文件，而是需要共享锁，还必须要选择 FlieShare 方式为 ReadWrite。（读方与写方都需要是 ReadWrite。）&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果要一个程序要监视另一个程序的写入，然后读取写入的内容，可参考 FileSystemWatcher。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Mon, 22 Sep 2014 14:36:00 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2014/09/22/FileShare-read-file-which-is-written-by-another-process.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2014/09/22/FileShare-read-file-which-is-written-by-another-process.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>Windows DPI Awareness for WPF</title>
        <description>&lt;p&gt;对于 WPF 程序，要控制程序的 DPI 感知程度，可在 App.manifest 中添加如下代码。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文知识已经陈旧，你可以阅读这两篇文章来了解更新的 Windows DPI 应用知识：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development&quot;&gt;Windows 下的高 DPI 应用开发（UWP / WPF / Windows Forms / Win32） - walterlv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/windows-high-dpi-development-for-wpf&quot;&gt;支持 Windows 10 最新 PerMonitorV2 特性的 WPF 高 DPI 应用开发 - walterlv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;原文内容：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;asmv3:application&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:asmv3=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-microsoft-com:asm.v3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;asmv3:windowsSettings&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.microsoft.com/SMI/2005/WindowsSettings&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 此应用程序将使用以下 DPI 感知级别。（默认情况下是系统 DPI 感知级别。）--&amp;gt;&lt;/span&gt;
      
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 应用程序不对 DPI 感知，将由 DWM （Desktop Window Manager）进行 DPI 缩放控制。 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;dpiAware&amp;gt;False&amp;lt;/dpiAware&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 应用程序具有系统级别的 DPI 感知能力。 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;dpiAware&amp;gt;True&amp;lt;/dpiAware&amp;gt;--&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 应用程序对每个显示器的 DPI 都具备感知能力。 --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;dpiAware&amp;gt;&lt;/span&gt;True/PM&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dpiAware&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/asmv3:windowsSettings&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/asmv3:application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 20 Sep 2014 08:56:00 +0000</pubDate>
        <link>https://blog.walterlv.com/windows/2014/09/20/windows-dpi-awareness-for-wpf.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/windows/2014/09/20/windows-dpi-awareness-for-wpf.html</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>CallerMemberName</title>
        <description>&lt;p&gt;从 .NET Framework 4.5 开始，有了几个快速获取调用方信息的 Attribute。&lt;/p&gt;

&lt;hr /&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;描述&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CallerMemberName&lt;/td&gt;
      &lt;td&gt;允许您打算调用方的方法或属性名称传递给方法。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CallerLineNumber&lt;/td&gt;
      &lt;td&gt;允许您打算在调用方法的源文件中的行号。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CallerFilePath&lt;/td&gt;
      &lt;td&gt;允许您获取包含调用方源文件的完整路径。这是文件路径在生成时。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这样，在实现用于 XAML 绑定的类型中，可以更方便更高效地进行属性更改通知。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyPropertyChangedInvocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnPropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallerMemberName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PropertyChangedEventHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertyChangedEventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当使用 OnPropertyChanged() 句子调用以上方法时，参数 propertyName 会被自动赋值。&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallerMemberName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;OnPropertyChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可在类型中添加以上方法，使得属性更改变得更加简单：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_notifyMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Notification&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_notifyMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_notifyMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;详细信息可阅读：&lt;br /&gt;
&lt;a href=&quot;http://10rem.net/blog/2013/02/25/using-callermembername-for-property-change-notification-in-xaml-apps&quot;&gt;Using CallerMemberName for property change notification in XAML apps&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Sep 2014 02:19:00 +0000</pubDate>
        <link>https://blog.walterlv.com/dotnet/2014/09/12/CallerMemberName.html</link>
        <guid isPermaLink="true">https://blog.walterlv.com/dotnet/2014/09/12/CallerMemberName.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
    
  </channel>
</rss>
